~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/******************************************************
2
Transaction undo log record
3
4
(c) 1996 Innobase Oy
5
6
Created 3/26/1996 Heikki Tuuri
7
*******************************************************/
8
9
#include "trx0rec.h"
10
11
#ifdef UNIV_NONINL
12
#include "trx0rec.ic"
13
#endif
14
15
#include "fsp0fsp.h"
16
#include "mach0data.h"
17
#include "trx0rseg.h"
18
#include "trx0trx.h"
19
#include "trx0undo.h"
20
#include "dict0dict.h"
21
#include "ut0mem.h"
22
#include "row0upd.h"
23
#include "que0que.h"
24
#include "trx0purge.h"
25
#include "row0row.h"
26
27
/*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
28
29
/**************************************************************************
30
Writes the mtr log entry of the inserted undo log record on the undo log
31
page. */
32
UNIV_INLINE
33
void
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 */
40
{
41
	byte*		log_ptr;
42
	const byte*	log_end;
43
	ulint		len;
44
45
	log_ptr = mlog_open(mtr, 11 + 13 + MLOG_BUF_MARGIN);
46
47
	if (log_ptr == NULL) {
48
49
		return;
50
	}
51
52
	log_end = &log_ptr[11 + 13 + MLOG_BUF_MARGIN];
53
	log_ptr = mlog_write_initial_log_record_fast(
54
		undo_page, MLOG_UNDO_INSERT, log_ptr, mtr);
55
	len = new_free - old_free - 4;
56
57
	mach_write_to_2(log_ptr, len);
58
	log_ptr += 2;
59
60
	if (log_ptr + len <= log_end) {
61
		memcpy(log_ptr, undo_page + old_free + 2, len);
62
		mlog_close(mtr, log_ptr + len);
63
	} else {
64
		mlog_close(mtr, log_ptr);
65
		mlog_catenate_string(mtr, undo_page + old_free + 2, len);
66
	}
67
}
68
69
/***************************************************************
70
Parses a redo log record of adding an undo log record. */
71
72
byte*
73
trx_undo_parse_add_undo_rec(
74
/*========================*/
75
			/* out: end of log record or NULL */
76
	byte*	ptr,	/* in: buffer */
77
	byte*	end_ptr,/* in: buffer end */
78
	page_t*	page)	/* in: page or NULL */
79
{
80
	ulint	len;
81
	byte*	rec;
82
	ulint	first_free;
83
84
	if (end_ptr < ptr + 2) {
85
86
		return(NULL);
87
	}
88
89
	len = mach_read_from_2(ptr);
90
	ptr += 2;
91
92
	if (end_ptr < ptr + len) {
93
94
		return(NULL);
95
	}
96
97
	if (page == NULL) {
98
99
		return(ptr + len);
100
	}
101
102
	first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
103
				      + TRX_UNDO_PAGE_FREE);
104
	rec = page + first_free;
105
106
	mach_write_to_2(rec, first_free + 4 + len);
107
	mach_write_to_2(rec + 2 + len, first_free);
108
109
	mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
110
			first_free + 4 + len);
111
	ut_memcpy(rec + 2, ptr, len);
112
113
	return(ptr + len);
114
}
115
116
/**************************************************************************
117
Calculates the free space left for extending an undo log record. */
118
UNIV_INLINE
119
ulint
120
trx_undo_left(
121
/*==========*/
122
			/* out: bytes left */
123
	page_t* page,	/* in: undo log page */
124
	byte*	ptr)	/* in: pointer to page */
125
{
126
	/* The '- 10' is a safety margin, in case we have some small
127
	calculation error below */
128
129
	return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
130
}
131
132
/**************************************************************************
133
Reports in the undo log of an insert of a clustered index record. */
134
static
135
ulint
136
trx_undo_page_report_insert(
137
/*========================*/
138
					/* out: offset of the inserted entry
139
					on the page if succeed, 0 if fail */
140
	page_t*		undo_page,	/* in: undo log page */
141
	trx_t*		trx,		/* in: transaction */
142
	dict_index_t*	index,		/* in: clustered index */
143
	dtuple_t*	clust_entry,	/* in: index entry which will be
144
					inserted to the clustered index */
145
	mtr_t*		mtr)		/* in: mtr */
146
{
147
	ulint		first_free;
148
	byte*		ptr;
149
	ulint		len;
150
	dfield_t*	field;
151
	ulint		flen;
152
	ulint		i;
153
154
	ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
155
			       + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
156
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
161
	ut_ad(first_free <= UNIV_PAGE_SIZE);
162
163
	if (trx_undo_left(undo_page, ptr) < 30) {
164
165
		/* NOTE: the value 30 must be big enough such that the general
166
		fields written below fit on the undo log page */
167
168
		return(0);
169
	}
170
171
	/* Reserve 2 bytes for the pointer to the next undo log record */
172
	ptr += 2;
173
174
	/* Store first some general parameters to the undo log */
175
	mach_write_to_1(ptr, TRX_UNDO_INSERT_REC);
176
	ptr++;
177
178
	len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
179
	ptr += len;
180
181
	len = mach_dulint_write_much_compressed(ptr, (index->table)->id);
182
	ptr += len;
183
	/*----------------------------------------*/
184
	/* Store then the fields required to uniquely determine the record
185
	to be inserted in the clustered index */
186
187
	for (i = 0; i < dict_index_get_n_unique(index); i++) {
188
189
		field = dtuple_get_nth_field(clust_entry, i);
190
191
		flen = dfield_get_len(field);
192
193
		if (trx_undo_left(undo_page, ptr) < 5) {
194
195
			return(0);
196
		}
197
198
		len = mach_write_compressed(ptr, flen);
199
		ptr += len;
200
201
		if (flen != UNIV_SQL_NULL) {
202
			if (trx_undo_left(undo_page, ptr) < flen) {
203
204
				return(0);
205
			}
206
207
			ut_memcpy(ptr, dfield_get_data(field), flen);
208
			ptr += flen;
209
		}
210
	}
211
212
	if (trx_undo_left(undo_page, ptr) < 2) {
213
214
		return(0);
215
	}
216
217
	/*----------------------------------------*/
218
	/* Write pointers to the previous and the next undo log records */
219
220
	if (trx_undo_left(undo_page, ptr) < 2) {
221
222
		return(0);
223
	}
224
225
	mach_write_to_2(ptr, first_free);
226
	ptr += 2;
227
228
	mach_write_to_2(undo_page + first_free, ptr - undo_page);
229
230
	mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
231
			ptr - undo_page);
232
233
	/* Write the log entry to the REDO log of this change in the UNDO
234
	log */
235
	trx_undof_page_add_undo_rec_log(undo_page, first_free,
236
					ptr - undo_page, mtr);
237
	return(first_free);
238
}
239
240
/**************************************************************************
241
Reads from an undo log record the general parameters. */
242
243
byte*
244
trx_undo_rec_get_pars(
245
/*==================*/
246
					/* out: remaining part of undo log
247
					record after reading these values */
248
	trx_undo_rec_t*	undo_rec,	/* in: undo log record */
249
	ulint*		type,		/* out: undo record type:
250
					TRX_UNDO_INSERT_REC, ... */
251
	ulint*		cmpl_info,	/* out: compiler info, relevant only
252
					for update type records */
253
	ibool*		updated_extern,	/* out: TRUE if we updated an
254
					externally stored fild */
255
	dulint*		undo_no,	/* out: undo log record number */
256
	dulint*		table_id)	/* out: table id */
257
{
258
	byte*		ptr;
259
	ulint		len;
260
	ulint		type_cmpl;
261
262
	ptr = undo_rec + 2;
263
264
	type_cmpl = mach_read_from_1(ptr);
265
	ptr++;
266
267
	if (type_cmpl & TRX_UNDO_UPD_EXTERN) {
268
		*updated_extern = TRUE;
269
		type_cmpl -= TRX_UNDO_UPD_EXTERN;
270
	} else {
271
		*updated_extern = FALSE;
272
	}
273
274
	*type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
275
	*cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
276
277
	*undo_no = mach_dulint_read_much_compressed(ptr);
278
	len = mach_dulint_get_much_compressed_size(*undo_no);
279
	ptr += len;
280
281
	*table_id = mach_dulint_read_much_compressed(ptr);
282
	len = mach_dulint_get_much_compressed_size(*table_id);
283
	ptr += len;
284
285
	return(ptr);
286
}
287
288
/**************************************************************************
289
Reads from an undo log record a stored column value. */
290
static
291
byte*
292
trx_undo_rec_get_col_val(
293
/*=====================*/
294
			/* out: remaining part of undo log record after
295
			reading these values */
296
	byte*	ptr,	/* in: pointer to remaining part of undo log record */
297
	byte**	field,	/* out: pointer to stored field */
298
	ulint*	len)	/* out: length of the field, or UNIV_SQL_NULL */
299
{
300
	*len = mach_read_compressed(ptr);
301
	ptr += mach_get_compressed_size(*len);
302
303
	*field = ptr;
304
305
	if (*len != UNIV_SQL_NULL) {
306
		if (*len >= UNIV_EXTERN_STORAGE_FIELD) {
307
			ptr += (*len - UNIV_EXTERN_STORAGE_FIELD);
308
		} else {
309
			ptr += *len;
310
		}
311
	}
312
313
	return(ptr);
314
}
315
316
/***********************************************************************
317
Builds a row reference from an undo log record. */
318
319
byte*
320
trx_undo_rec_get_row_ref(
321
/*=====================*/
322
				/* out: pointer to remaining part of undo
323
				record */
324
	byte*		ptr,	/* in: remaining part of a copy of an undo log
325
				record, at the start of the row reference;
326
				NOTE that this copy of the undo log record must
327
				be preserved as long as the row reference is
328
				used, as we do NOT copy the data in the
329
				record! */
330
	dict_index_t*	index,	/* in: clustered index */
331
	dtuple_t**	ref,	/* out, own: row reference */
332
	mem_heap_t*	heap)	/* in: memory heap from which the memory
333
				needed is allocated */
334
{
335
	dfield_t*	dfield;
336
	byte*		field;
337
	ulint		len;
338
	ulint		ref_len;
339
	ulint		i;
340
341
	ut_ad(index && ptr && ref && heap);
342
	ut_a(index->type & DICT_CLUSTERED);
343
344
	ref_len = dict_index_get_n_unique(index);
345
346
	*ref = dtuple_create(heap, ref_len);
347
348
	dict_index_copy_types(*ref, index, ref_len);
349
350
	for (i = 0; i < ref_len; i++) {
351
		dfield = dtuple_get_nth_field(*ref, i);
352
353
		ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
354
355
		dfield_set_data(dfield, field, len);
356
	}
357
358
	return(ptr);
359
}
360
361
/***********************************************************************
362
Skips a row reference from an undo log record. */
363
364
byte*
365
trx_undo_rec_skip_row_ref(
366
/*======================*/
367
				/* out: pointer to remaining part of undo
368
				record */
369
	byte*		ptr,	/* in: remaining part in update undo log
370
				record, at the start of the row reference */
371
	dict_index_t*	index)	/* in: clustered index */
372
{
373
	byte*	field;
374
	ulint	len;
375
	ulint	ref_len;
376
	ulint	i;
377
378
	ut_ad(index && ptr);
379
	ut_a(index->type & DICT_CLUSTERED);
380
381
	ref_len = dict_index_get_n_unique(index);
382
383
	for (i = 0; i < ref_len; i++) {
384
		ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
385
	}
386
387
	return(ptr);
388
}
389
390
/**************************************************************************
391
Reports in the undo log of an update or delete marking of a clustered index
392
record. */
393
static
394
ulint
395
trx_undo_page_report_modify(
396
/*========================*/
397
					/* out: byte offset of the inserted
398
					undo log entry on the page if succeed,
399
					0 if fail */
400
	page_t*		undo_page,	/* in: undo log page */
401
	trx_t*		trx,		/* in: transaction */
402
	dict_index_t*	index,		/* in: clustered index where update or
403
					delete marking is done */
404
	rec_t*		rec,		/* in: clustered index record which
405
					has NOT yet been modified */
406
	const ulint*	offsets,	/* in: rec_get_offsets(rec, index) */
407
	upd_t*		update,		/* in: update vector which tells the
408
					columns to be updated; in the case of
409
					a delete, this should be set to NULL */
410
	ulint		cmpl_info,	/* in: compiler info on secondary
411
					index updates */
412
	mtr_t*		mtr)		/* in: mtr */
413
{
414
	dict_table_t*	table;
415
	upd_field_t*	upd_field;
416
	ulint		first_free;
417
	byte*		ptr;
418
	ulint		len;
419
	byte*		field;
420
	ulint		flen;
421
	ulint		pos;
422
	dulint		roll_ptr;
423
	dulint		trx_id;
424
	ulint		bits;
425
	ulint		col_no;
426
	byte*		old_ptr;
427
	ulint		type_cmpl;
428
	byte*		type_cmpl_ptr;
429
	ulint		i;
430
431
	ut_a(index->type & DICT_CLUSTERED);
432
	ut_ad(rec_offs_validate(rec, index, offsets));
433
	ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
434
			       + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE);
435
	table = index->table;
436
437
	first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
438
				      + TRX_UNDO_PAGE_FREE);
439
	ptr = undo_page + first_free;
440
441
	ut_ad(first_free <= UNIV_PAGE_SIZE);
442
443
	if (trx_undo_left(undo_page, ptr) < 50) {
444
445
		/* NOTE: the value 50 must be big enough so that the general
446
		fields written below fit on the undo log page */
447
448
		return(0);
449
	}
450
451
	/* Reserve 2 bytes for the pointer to the next undo log record */
452
	ptr += 2;
453
454
	/* Store first some general parameters to the undo log */
455
456
	if (update) {
457
		if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) {
458
			type_cmpl = TRX_UNDO_UPD_DEL_REC;
459
		} else {
460
			type_cmpl = TRX_UNDO_UPD_EXIST_REC;
461
		}
462
	} else {
463
		type_cmpl = TRX_UNDO_DEL_MARK_REC;
464
	}
465
466
	type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT);
467
468
	mach_write_to_1(ptr, type_cmpl);
469
470
	type_cmpl_ptr = ptr;
471
472
	ptr++;
473
	len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
474
	ptr += len;
475
476
	len = mach_dulint_write_much_compressed(ptr, table->id);
477
	ptr += len;
478
479
	/*----------------------------------------*/
480
	/* Store the state of the info bits */
481
482
	bits = rec_get_info_bits(rec, dict_table_is_comp(table));
483
	mach_write_to_1(ptr, bits);
484
	ptr += 1;
485
486
	/* Store the values of the system columns */
487
	field = rec_get_nth_field(rec, offsets,
488
				  dict_index_get_sys_col_pos(
489
					  index, DATA_TRX_ID), &len);
490
	ut_ad(len == DATA_TRX_ID_LEN);
491
	trx_id = trx_read_trx_id(field);
492
	field = rec_get_nth_field(rec, offsets,
493
				  dict_index_get_sys_col_pos(
494
					  index, DATA_ROLL_PTR), &len);
495
	ut_ad(len == DATA_ROLL_PTR_LEN);
496
	roll_ptr = trx_read_roll_ptr(field);
497
498
	len = mach_dulint_write_compressed(ptr, trx_id);
499
	ptr += len;
500
501
	len = mach_dulint_write_compressed(ptr, roll_ptr);
502
	ptr += len;
503
504
	/*----------------------------------------*/
505
	/* Store then the fields required to uniquely determine the
506
	record which will be modified in the clustered index */
507
508
	for (i = 0; i < dict_index_get_n_unique(index); i++) {
509
510
		field = rec_get_nth_field(rec, offsets, i, &flen);
511
512
		if (trx_undo_left(undo_page, ptr) < 4) {
513
514
			return(0);
515
		}
516
517
		len = mach_write_compressed(ptr, flen);
518
		ptr += len;
519
520
		if (flen != UNIV_SQL_NULL) {
521
			if (trx_undo_left(undo_page, ptr) < flen) {
522
523
				return(0);
524
			}
525
526
			ut_memcpy(ptr, field, flen);
527
			ptr += flen;
528
		}
529
	}
530
531
	/*----------------------------------------*/
532
	/* Save to the undo log the old values of the columns to be updated. */
533
534
	if (update) {
535
		if (trx_undo_left(undo_page, ptr) < 5) {
536
537
			return(0);
538
		}
539
540
		len = mach_write_compressed(ptr, upd_get_n_fields(update));
541
		ptr += len;
542
543
		for (i = 0; i < upd_get_n_fields(update); i++) {
544
545
			upd_field = upd_get_nth_field(update, i);
546
			pos = upd_field->field_no;
547
548
			/* Write field number to undo log */
549
			if (trx_undo_left(undo_page, ptr) < 5) {
550
551
				return(0);
552
			}
553
554
			len = mach_write_compressed(ptr, pos);
555
			ptr += len;
556
557
			/* Save the old value of field */
558
			field = rec_get_nth_field(rec, offsets, pos, &flen);
559
560
			if (trx_undo_left(undo_page, ptr) < 5) {
561
562
				return(0);
563
			}
564
565
			if (rec_offs_nth_extern(offsets, pos)) {
566
				/* If a field has external storage, we add
567
				to flen the flag */
568
569
				len = mach_write_compressed(
570
					ptr,
571
					UNIV_EXTERN_STORAGE_FIELD + flen);
572
573
				/* Notify purge that it eventually has to
574
				free the old externally stored field */
575
576
				trx->update_undo->del_marks = TRUE;
577
578
				*type_cmpl_ptr = *type_cmpl_ptr
579
					| TRX_UNDO_UPD_EXTERN;
580
			} else {
581
				len = mach_write_compressed(ptr, flen);
582
			}
583
584
			ptr += len;
585
586
			if (flen != UNIV_SQL_NULL) {
587
				if (trx_undo_left(undo_page, ptr) < flen) {
588
589
					return(0);
590
				}
591
592
				ut_memcpy(ptr, field, flen);
593
				ptr += flen;
594
			}
595
		}
596
	}
597
598
	/*----------------------------------------*/
599
	/* In the case of a delete marking, and also in the case of an update
600
	where any ordering field of any index changes, store the values of all
601
	columns which occur as ordering fields in any index. This info is used
602
	in the purge of old versions where we use it to build and search the
603
	delete marked index records, to look if we can remove them from the
604
	index tree. Note that starting from 4.0.14 also externally stored
605
	fields can be ordering in some index. But we always store at least
606
	384 first bytes locally to the clustered index record, which means
607
	we can construct the column prefix fields in the index from the
608
	stored data. */
609
610
	if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
611
612
		trx->update_undo->del_marks = TRUE;
613
614
		if (trx_undo_left(undo_page, ptr) < 5) {
615
616
			return(0);
617
		}
618
619
		old_ptr = ptr;
620
621
		/* Reserve 2 bytes to write the number of bytes the stored
622
		fields take in this undo record */
623
624
		ptr += 2;
625
626
		for (col_no = 0; col_no < dict_table_get_n_cols(table);
627
		     col_no++) {
628
629
			const dict_col_t*	col
630
				= dict_table_get_nth_col(table, col_no);
631
632
			if (col->ord_part > 0) {
633
634
				pos = dict_index_get_nth_col_pos(index,
635
								 col_no);
636
637
				/* Write field number to undo log */
638
				if (trx_undo_left(undo_page, ptr) < 5) {
639
640
					return(0);
641
				}
642
643
				len = mach_write_compressed(ptr, pos);
644
				ptr += len;
645
646
				/* Save the old value of field */
647
				field = rec_get_nth_field(rec, offsets, pos,
648
							  &flen);
649
650
				if (trx_undo_left(undo_page, ptr) < 5) {
651
652
					return(0);
653
				}
654
655
				len = mach_write_compressed(ptr, flen);
656
				ptr += len;
657
658
				if (flen != UNIV_SQL_NULL) {
659
					if (trx_undo_left(undo_page, ptr)
660
					    < flen) {
661
662
						return(0);
663
					}
664
665
					ut_memcpy(ptr, field, flen);
666
					ptr += flen;
667
				}
668
			}
669
		}
670
671
		mach_write_to_2(old_ptr, ptr - old_ptr);
672
	}
673
674
	/*----------------------------------------*/
675
	/* Write pointers to the previous and the next undo log records */
676
	if (trx_undo_left(undo_page, ptr) < 2) {
677
678
		return(0);
679
	}
680
681
	mach_write_to_2(ptr, first_free);
682
	ptr += 2;
683
	mach_write_to_2(undo_page + first_free, ptr - undo_page);
684
685
	mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
686
			ptr - undo_page);
687
688
	/* Write to the REDO log about this change in the UNDO log */
689
690
	trx_undof_page_add_undo_rec_log(undo_page, first_free,
691
					ptr - undo_page, mtr);
692
	return(first_free);
693
}
694
695
/**************************************************************************
696
Reads from an undo log update record the system field values of the old
697
version. */
698
699
byte*
700
trx_undo_update_rec_get_sys_cols(
701
/*=============================*/
702
				/* out: remaining part of undo log
703
				record after reading these values */
704
	byte*	ptr,		/* in: remaining part of undo log
705
				record after reading general
706
				parameters */
707
	dulint*	trx_id,		/* out: trx id */
708
	dulint*	roll_ptr,	/* out: roll ptr */
709
	ulint*	info_bits)	/* out: info bits state */
710
{
711
	ulint	len;
712
713
	/* Read the state of the info bits */
714
	*info_bits = mach_read_from_1(ptr);
715
	ptr += 1;
716
717
	/* Read the values of the system columns */
718
719
	*trx_id = mach_dulint_read_compressed(ptr);
720
	len = mach_dulint_get_compressed_size(*trx_id);
721
	ptr += len;
722
723
	*roll_ptr = mach_dulint_read_compressed(ptr);
724
	len = mach_dulint_get_compressed_size(*roll_ptr);
725
	ptr += len;
726
727
	return(ptr);
728
}
729
730
/**************************************************************************
731
Reads from an update undo log record the number of updated fields. */
732
UNIV_INLINE
733
byte*
734
trx_undo_update_rec_get_n_upd_fields(
735
/*=================================*/
736
			/* out: remaining part of undo log record after
737
			reading this value */
738
	byte*	ptr,	/* in: pointer to remaining part of undo log record */
739
	ulint*	n)	/* out: number of fields */
740
{
741
	*n = mach_read_compressed(ptr);
742
	ptr += mach_get_compressed_size(*n);
743
744
	return(ptr);
745
}
746
747
/**************************************************************************
748
Reads from an update undo log record a stored field number. */
749
UNIV_INLINE
750
byte*
751
trx_undo_update_rec_get_field_no(
752
/*=============================*/
753
			/* out: remaining part of undo log record after
754
			reading this value */
755
	byte*	ptr,	/* in: pointer to remaining part of undo log record */
756
	ulint*	field_no)/* out: field number */
757
{
758
	*field_no = mach_read_compressed(ptr);
759
	ptr += mach_get_compressed_size(*field_no);
760
761
	return(ptr);
762
}
763
764
/***********************************************************************
765
Builds an update vector based on a remaining part of an undo log record. */
766
767
byte*
768
trx_undo_update_rec_get_update(
769
/*===========================*/
770
				/* out: remaining part of the record,
771
				NULL if an error detected, which means that
772
				the record is corrupted */
773
	byte*		ptr,	/* in: remaining part in update undo log
774
				record, after reading the row reference
775
				NOTE that this copy of the undo log record must
776
				be preserved as long as the update vector is
777
				used, as we do NOT copy the data in the
778
				record! */
779
	dict_index_t*	index,	/* in: clustered index */
780
	ulint		type,	/* in: TRX_UNDO_UPD_EXIST_REC,
781
				TRX_UNDO_UPD_DEL_REC, or
782
				TRX_UNDO_DEL_MARK_REC; in the last case,
783
				only trx id and roll ptr fields are added to
784
				the update vector */
785
	dulint		trx_id,	/* in: transaction id from this undo record */
786
	dulint		roll_ptr,/* in: roll pointer from this undo record */
787
	ulint		info_bits,/* in: info bits from this undo record */
788
	trx_t*		trx,	/* in: transaction */
789
	mem_heap_t*	heap,	/* in: memory heap from which the memory
790
				needed is allocated */
791
	upd_t**		upd)	/* out, own: update vector */
792
{
793
	upd_field_t*	upd_field;
794
	upd_t*		update;
795
	ulint		n_fields;
796
	byte*		buf;
797
	byte*		field;
798
	ulint		len;
799
	ulint		field_no;
800
	ulint		i;
801
802
	ut_a(index->type & DICT_CLUSTERED);
803
804
	if (type != TRX_UNDO_DEL_MARK_REC) {
805
		ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
806
	} else {
807
		n_fields = 0;
808
	}
809
810
	update = upd_create(n_fields + 2, heap);
811
812
	update->info_bits = info_bits;
813
814
	/* Store first trx id and roll ptr to update vector */
815
816
	upd_field = upd_get_nth_field(update, n_fields);
817
	buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN);
818
	trx_write_trx_id(buf, trx_id);
819
820
	upd_field_set_field_no(upd_field,
821
			       dict_index_get_sys_col_pos(index, DATA_TRX_ID),
822
			       index, trx);
823
	dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
824
825
	upd_field = upd_get_nth_field(update, n_fields + 1);
826
	buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN);
827
	trx_write_roll_ptr(buf, roll_ptr);
828
829
	upd_field_set_field_no(
830
		upd_field, dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
831
		index, trx);
832
	dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
833
834
	/* Store then the updated ordinary columns to the update vector */
835
836
	for (i = 0; i < n_fields; i++) {
837
838
		ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
839
840
		if (field_no >= dict_index_get_n_fields(index)) {
841
			fprintf(stderr,
842
				"InnoDB: Error: trying to access"
843
				" update undo rec field %lu in ",
844
				(ulong) field_no);
845
			dict_index_name_print(stderr, trx, index);
846
			fprintf(stderr, "\n"
847
				"InnoDB: but index has only %lu fields\n"
848
				"InnoDB: Submit a detailed bug report"
849
				" to http://bugs.mysql.com\n"
850
				"InnoDB: Run also CHECK TABLE ",
851
				(ulong) dict_index_get_n_fields(index));
852
			ut_print_name(stderr, trx, TRUE, index->table_name);
853
			fprintf(stderr, "\n"
854
				"InnoDB: n_fields = %lu, i = %lu, ptr %p\n",
855
				(ulong) n_fields, (ulong) i, ptr);
856
			return(NULL);
857
		}
858
859
		ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
860
861
		upd_field = upd_get_nth_field(update, i);
862
863
		upd_field_set_field_no(upd_field, field_no, index, trx);
864
865
		if (len != UNIV_SQL_NULL && len >= UNIV_EXTERN_STORAGE_FIELD) {
866
867
			upd_field->extern_storage = TRUE;
868
869
			len -= UNIV_EXTERN_STORAGE_FIELD;
870
		}
871
872
		dfield_set_data(&(upd_field->new_val), field, len);
873
	}
874
875
	*upd = update;
876
877
	return(ptr);
878
}
879
880
/***********************************************************************
881
Builds a partial row from an update undo log record. It contains the
882
columns which occur as ordering in any index of the table. */
883
884
byte*
885
trx_undo_rec_get_partial_row(
886
/*=========================*/
887
				/* out: pointer to remaining part of undo
888
				record */
889
	byte*		ptr,	/* in: remaining part in update undo log
890
				record of a suitable type, at the start of
891
				the stored index columns;
892
				NOTE that this copy of the undo log record must
893
				be preserved as long as the partial row is
894
				used, as we do NOT copy the data in the
895
				record! */
896
	dict_index_t*	index,	/* in: clustered index */
897
	dtuple_t**	row,	/* out, own: partial row */
898
	mem_heap_t*	heap)	/* in: memory heap from which the memory
899
				needed is allocated */
900
{
901
	dfield_t*	dfield;
902
	byte*		field;
903
	ulint		len;
904
	ulint		field_no;
905
	ulint		col_no;
906
	ulint		row_len;
907
	ulint		total_len;
908
	byte*		start_ptr;
909
	ulint		i;
910
911
	ut_ad(index && ptr && row && heap);
912
913
	row_len = dict_table_get_n_cols(index->table);
914
915
	*row = dtuple_create(heap, row_len);
916
917
	dict_table_copy_types(*row, index->table);
918
919
	start_ptr = ptr;
920
921
	total_len = mach_read_from_2(ptr);
922
	ptr += 2;
923
924
	for (i = 0;; i++) {
925
926
		if (ptr == start_ptr + total_len) {
927
928
			break;
929
		}
930
931
		ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
932
933
		col_no = dict_index_get_nth_col_no(index, field_no);
934
935
		ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
936
937
		dfield = dtuple_get_nth_field(*row, col_no);
938
939
		dfield_set_data(dfield, field, len);
940
	}
941
942
	return(ptr);
943
}
944
945
/***************************************************************************
946
Erases the unused undo log page end. */
947
static
948
void
949
trx_undo_erase_page_end(
950
/*====================*/
951
	page_t*	undo_page,	/* in: undo page whose end to erase */
952
	mtr_t*	mtr)		/* in: mtr */
953
{
954
	ulint	first_free;
955
956
	first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
957
				      + TRX_UNDO_PAGE_FREE);
958
	memset(undo_page + first_free, 0xff,
959
	       (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END) - first_free);
960
961
	mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
962
}
963
964
/***************************************************************
965
Parses a redo log record of erasing of an undo page end. */
966
967
byte*
968
trx_undo_parse_erase_page_end(
969
/*==========================*/
970
			/* out: end of log record or NULL */
971
	byte*	ptr,	/* in: buffer */
972
	byte*	end_ptr __attribute__((unused)), /* in: buffer end */
973
	page_t*	page,	/* in: page or NULL */
974
	mtr_t*	mtr)	/* in: mtr or NULL */
975
{
976
	ut_ad(ptr && end_ptr);
977
978
	if (page == NULL) {
979
980
		return(ptr);
981
	}
982
983
	trx_undo_erase_page_end(page, mtr);
984
985
	return(ptr);
986
}
987
988
/***************************************************************************
989
Writes information to an undo log about an insert, update, or a delete marking
990
of a clustered index record. This information is used in a rollback of the
991
transaction and in consistent reads that must look to the history of this
992
transaction. */
993
994
ulint
995
trx_undo_report_row_operation(
996
/*==========================*/
997
					/* out: DB_SUCCESS or error code */
998
	ulint		flags,		/* in: if BTR_NO_UNDO_LOG_FLAG bit is
999
					set, does nothing */
1000
	ulint		op_type,	/* in: TRX_UNDO_INSERT_OP or
1001
					TRX_UNDO_MODIFY_OP */
1002
	que_thr_t*	thr,		/* in: query thread */
1003
	dict_index_t*	index,		/* in: clustered index */
1004
	dtuple_t*	clust_entry,	/* in: in the case of an insert,
1005
					index entry to insert into the
1006
					clustered index, otherwise NULL */
1007
	upd_t*		update,		/* in: in the case of an update,
1008
					the update vector, otherwise NULL */
1009
	ulint		cmpl_info,	/* in: compiler info on secondary
1010
					index updates */
1011
	rec_t*		rec,		/* in: in case of an update or delete
1012
					marking, the record in the clustered
1013
					index, otherwise NULL */
1014
	dulint*		roll_ptr)	/* out: rollback pointer to the
1015
					inserted undo log record,
1016
					ut_dulint_zero if BTR_NO_UNDO_LOG
1017
					flag was specified */
1018
{
1019
	trx_t*		trx;
1020
	trx_undo_t*	undo;
1021
	page_t*		undo_page;
1022
	ulint		offset;
1023
	ulint		page_no;
1024
	ibool		is_insert;
1025
	trx_rseg_t*	rseg;
1026
	mtr_t		mtr;
1027
	ulint		err		= DB_SUCCESS;
1028
	mem_heap_t*	heap		= NULL;
1029
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
1030
	ulint*		offsets		= offsets_;
1031
	*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1032
1033
	ut_a(index->type & DICT_CLUSTERED);
1034
1035
	if (flags & BTR_NO_UNDO_LOG_FLAG) {
1036
1037
		*roll_ptr = ut_dulint_zero;
1038
1039
		return(err);
1040
	}
1041
1042
	ut_ad(thr);
1043
	ut_ad((op_type != TRX_UNDO_INSERT_OP)
1044
	      || (clust_entry && !update && !rec));
1045
1046
	trx = thr_get_trx(thr);
1047
	rseg = trx->rseg;
1048
1049
	mutex_enter(&(trx->undo_mutex));
1050
1051
	/* If the undo log is not assigned yet, assign one */
1052
1053
	if (op_type == TRX_UNDO_INSERT_OP) {
1054
1055
		if (trx->insert_undo == NULL) {
1056
1057
			err = trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
1058
		}
1059
1060
		undo = trx->insert_undo;
1061
		is_insert = TRUE;
1062
	} else {
1063
		ut_ad(op_type == TRX_UNDO_MODIFY_OP);
1064
1065
		if (trx->update_undo == NULL) {
1066
1067
			err = trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
1068
1069
		}
1070
1071
		undo = trx->update_undo;
1072
		is_insert = FALSE;
1073
	}
1074
1075
	if (err != DB_SUCCESS) {
1076
		/* Did not succeed: return the error encountered */
1077
		mutex_exit(&(trx->undo_mutex));
1078
1079
		return(err);
1080
	}
1081
1082
	page_no = undo->last_page_no;
1083
1084
	mtr_start(&mtr);
1085
1086
	for (;;) {
1087
		undo_page = buf_page_get_gen(undo->space, page_no,
1088
					     RW_X_LATCH, undo->guess_page,
1089
					     BUF_GET,
1090
					     __FILE__, __LINE__,
1091
					     &mtr);
1092
1093
#ifdef UNIV_SYNC_DEBUG
1094
		buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE);
1095
#endif /* UNIV_SYNC_DEBUG */
1096
1097
		if (op_type == TRX_UNDO_INSERT_OP) {
1098
			offset = trx_undo_page_report_insert(
1099
				undo_page, trx, index, clust_entry, &mtr);
1100
		} else {
1101
			offsets = rec_get_offsets(rec, index, offsets,
1102
						  ULINT_UNDEFINED, &heap);
1103
			offset = trx_undo_page_report_modify(
1104
				undo_page, trx, index, rec, offsets, update,
1105
				cmpl_info, &mtr);
1106
		}
1107
1108
		if (offset == 0) {
1109
			/* The record did not fit on the page. We erase the
1110
			end segment of the undo log page and write a log
1111
			record of it: this is to ensure that in the debug
1112
			version the replicate page constructed using the log
1113
			records stays identical to the original page */
1114
1115
			trx_undo_erase_page_end(undo_page, &mtr);
1116
		}
1117
1118
		mtr_commit(&mtr);
1119
1120
		if (offset != 0) {
1121
			/* Success */
1122
1123
			break;
1124
		}
1125
1126
		ut_ad(page_no == undo->last_page_no);
1127
1128
		/* We have to extend the undo log by one page */
1129
1130
		mtr_start(&mtr);
1131
1132
		/* When we add a page to an undo log, this is analogous to
1133
		a pessimistic insert in a B-tree, and we must reserve the
1134
		counterpart of the tree latch, which is the rseg mutex. */
1135
1136
		mutex_enter(&(rseg->mutex));
1137
1138
		page_no = trx_undo_add_page(trx, undo, &mtr);
1139
1140
		mutex_exit(&(rseg->mutex));
1141
1142
		if (page_no == FIL_NULL) {
1143
			/* Did not succeed: out of space */
1144
1145
			mutex_exit(&(trx->undo_mutex));
1146
			mtr_commit(&mtr);
1147
			if (UNIV_LIKELY_NULL(heap)) {
1148
				mem_heap_free(heap);
1149
			}
1150
			return(DB_OUT_OF_FILE_SPACE);
1151
		}
1152
	}
1153
1154
	undo->empty = FALSE;
1155
	undo->top_page_no = page_no;
1156
	undo->top_offset  = offset;
1157
	undo->top_undo_no = trx->undo_no;
1158
	undo->guess_page = undo_page;
1159
1160
	UT_DULINT_INC(trx->undo_no);
1161
1162
	mutex_exit(&(trx->undo_mutex));
1163
1164
	*roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
1165
					    offset);
1166
	if (UNIV_LIKELY_NULL(heap)) {
1167
		mem_heap_free(heap);
1168
	}
1169
	return(err);
1170
}
1171
1172
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
1173
1174
/**********************************************************************
1175
Copies an undo record to heap. This function can be called if we know that
1176
the undo log record exists. */
1177
1178
trx_undo_rec_t*
1179
trx_undo_get_undo_rec_low(
1180
/*======================*/
1181
					/* out, own: copy of the record */
1182
	dulint		roll_ptr,	/* in: roll pointer to record */
1183
	mem_heap_t*	heap)		/* in: memory heap where copied */
1184
{
1185
	trx_undo_rec_t*	undo_rec;
1186
	ulint		rseg_id;
1187
	ulint		page_no;
1188
	ulint		offset;
1189
	page_t*		undo_page;
1190
	trx_rseg_t*	rseg;
1191
	ibool		is_insert;
1192
	mtr_t		mtr;
1193
1194
	trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
1195
				 &offset);
1196
	rseg = trx_rseg_get_on_id(rseg_id);
1197
1198
	mtr_start(&mtr);
1199
1200
	undo_page = trx_undo_page_get_s_latched(rseg->space, page_no, &mtr);
1201
1202
	undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
1203
1204
	mtr_commit(&mtr);
1205
1206
	return(undo_rec);
1207
}
1208
1209
/**********************************************************************
1210
Copies an undo record to heap. */
1211
1212
ulint
1213
trx_undo_get_undo_rec(
1214
/*==================*/
1215
					/* out: DB_SUCCESS, or
1216
					DB_MISSING_HISTORY if the undo log
1217
					has been truncated and we cannot
1218
					fetch the old version; NOTE: the
1219
					caller must have latches on the
1220
					clustered index page and purge_view */
1221
	dulint		roll_ptr,	/* in: roll pointer to record */
1222
	dulint		trx_id,		/* in: id of the trx that generated
1223
					the roll pointer: it points to an
1224
					undo log of this transaction */
1225
	trx_undo_rec_t** undo_rec,	/* out, own: copy of the record */
1226
	mem_heap_t*	heap)		/* in: memory heap where copied */
1227
{
1228
#ifdef UNIV_SYNC_DEBUG
1229
	ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
1230
#endif /* UNIV_SYNC_DEBUG */
1231
1232
	if (!trx_purge_update_undo_must_exist(trx_id)) {
1233
1234
		/* It may be that the necessary undo log has already been
1235
		deleted */
1236
1237
		return(DB_MISSING_HISTORY);
1238
	}
1239
1240
	*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
1241
1242
	return(DB_SUCCESS);
1243
}
1244
1245
/***********************************************************************
1246
Build a previous version of a clustered index record. This function checks
1247
that the caller has a latch on the index page of the clustered index record
1248
and an s-latch on the purge_view. This guarantees that the stack of versions
1249
is locked. */
1250
1251
ulint
1252
trx_undo_prev_version_build(
1253
/*========================*/
1254
				/* out: DB_SUCCESS, or DB_MISSING_HISTORY if
1255
				the previous version is not >= purge_view,
1256
				which means that it may have been removed,
1257
				DB_ERROR if corrupted record */
1258
	rec_t*		index_rec,/* in: clustered index record in the
1259
				index tree */
1260
	mtr_t*		index_mtr __attribute__((unused)),
1261
				/* in: mtr which contains the latch to
1262
				index_rec page and purge_view */
1263
	rec_t*		rec,	/* in: version of a clustered index record */
1264
	dict_index_t*	index,	/* in: clustered index */
1265
	ulint*		offsets,/* in: rec_get_offsets(rec, index) */
1266
	mem_heap_t*	heap,	/* in: memory heap from which the memory
1267
				needed is allocated */
1268
	rec_t**		old_vers)/* out, own: previous version, or NULL if
1269
				rec is the first inserted version, or if
1270
				history data has been deleted */
1271
{
1272
	trx_undo_rec_t*	undo_rec;
1273
	dtuple_t*	entry;
1274
	dulint		rec_trx_id;
1275
	ulint		type;
1276
	dulint		undo_no;
1277
	dulint		table_id;
1278
	dulint		trx_id;
1279
	dulint		roll_ptr;
1280
	dulint		old_roll_ptr;
1281
	upd_t*		update;
1282
	byte*		ptr;
1283
	ulint		info_bits;
1284
	ulint		cmpl_info;
1285
	ibool		dummy_extern;
1286
	byte*		buf;
1287
	ulint		err;
1288
#ifdef UNIV_SYNC_DEBUG
1289
	ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
1290
#endif /* UNIV_SYNC_DEBUG */
1291
	ut_ad(mtr_memo_contains(index_mtr, buf_block_align(index_rec),
1292
				MTR_MEMO_PAGE_S_FIX)
1293
	      || mtr_memo_contains(index_mtr, buf_block_align(index_rec),
1294
				   MTR_MEMO_PAGE_X_FIX));
1295
	ut_ad(rec_offs_validate(rec, index, offsets));
1296
1297
	if (!(index->type & DICT_CLUSTERED)) {
1298
		fprintf(stderr, "InnoDB: Error: trying to access"
1299
			" update undo rec for non-clustered index %s\n"
1300
			"InnoDB: Submit a detailed bug report to"
1301
			" http://bugs.mysql.com\n"
1302
			"InnoDB: index record ", index->name);
1303
		rec_print(stderr, index_rec, index);
1304
		fputs("\n"
1305
		      "InnoDB: record version ", stderr);
1306
		rec_print_new(stderr, rec, offsets);
1307
		putc('\n', stderr);
1308
		return(DB_ERROR);
1309
	}
1310
1311
	roll_ptr = row_get_rec_roll_ptr(rec, index, offsets);
1312
	old_roll_ptr = roll_ptr;
1313
1314
	*old_vers = NULL;
1315
1316
	if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
1317
1318
		/* The record rec is the first inserted version */
1319
1320
		return(DB_SUCCESS);
1321
	}
1322
1323
	rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
1324
1325
	err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
1326
1327
	if (err != DB_SUCCESS) {
1328
1329
		return(err);
1330
	}
1331
1332
	ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info,
1333
				    &dummy_extern, &undo_no, &table_id);
1334
1335
	ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
1336
					       &info_bits);
1337
	ptr = trx_undo_rec_skip_row_ref(ptr, index);
1338
1339
	ptr = trx_undo_update_rec_get_update(ptr, index, type, trx_id,
1340
					     roll_ptr, info_bits,
1341
					     NULL, heap, &update);
1342
1343
	if (ut_dulint_cmp(table_id, index->table->id) != 0) {
1344
		ptr = NULL;
1345
1346
		fprintf(stderr,
1347
			"InnoDB: Error: trying to access update undo rec"
1348
			" for table %s\n"
1349
			"InnoDB: but the table id in the"
1350
			" undo record is wrong\n"
1351
			"InnoDB: Submit a detailed bug report"
1352
			" to http://bugs.mysql.com\n"
1353
			"InnoDB: Run also CHECK TABLE %s\n",
1354
			index->table_name, index->table_name);
1355
	}
1356
1357
	if (ptr == NULL) {
1358
		/* The record was corrupted, return an error; these printfs
1359
		should catch an elusive bug in row_vers_old_has_index_entry */
1360
1361
		fprintf(stderr,
1362
			"InnoDB: table %s, index %s, n_uniq %lu\n"
1363
			"InnoDB: undo rec address %p, type %lu cmpl_info %lu\n"
1364
			"InnoDB: undo rec table id %lu %lu,"
1365
			" index table id %lu %lu\n"
1366
			"InnoDB: dump of 150 bytes in undo rec: ",
1367
			index->table_name, index->name,
1368
			(ulong) dict_index_get_n_unique(index),
1369
			undo_rec, (ulong) type, (ulong) cmpl_info,
1370
			(ulong) ut_dulint_get_high(table_id),
1371
			(ulong) ut_dulint_get_low(table_id),
1372
			(ulong) ut_dulint_get_high(index->table->id),
1373
			(ulong) ut_dulint_get_low(index->table->id));
1374
		ut_print_buf(stderr, undo_rec, 150);
1375
		fputs("\n"
1376
		      "InnoDB: index record ", stderr);
1377
		rec_print(stderr, index_rec, index);
1378
		fputs("\n"
1379
		      "InnoDB: record version ", stderr);
1380
		rec_print_new(stderr, rec, offsets);
1381
		fprintf(stderr, "\n"
1382
			"InnoDB: Record trx id %lu %lu, update rec"
1383
			" trx id %lu %lu\n"
1384
			"InnoDB: Roll ptr in rec %lu %lu, in update rec"
1385
			" %lu %lu\n",
1386
			(ulong) ut_dulint_get_high(rec_trx_id),
1387
			(ulong) ut_dulint_get_low(rec_trx_id),
1388
			(ulong) ut_dulint_get_high(trx_id),
1389
			(ulong) ut_dulint_get_low(trx_id),
1390
			(ulong) ut_dulint_get_high(old_roll_ptr),
1391
			(ulong) ut_dulint_get_low(old_roll_ptr),
1392
			(ulong) ut_dulint_get_high(roll_ptr),
1393
			(ulong) ut_dulint_get_low(roll_ptr));
1394
1395
		trx_purge_sys_print();
1396
		return(DB_ERROR);
1397
	}
1398
1399
	if (row_upd_changes_field_size_or_external(index, offsets, update)) {
1400
		ulint*	ext_vect;
1401
		ulint	n_ext_vect;
1402
1403
		/* We have to set the appropriate extern storage bits in the
1404
		old version of the record: the extern bits in rec for those
1405
		fields that update does NOT update, as well as the the bits for
1406
		those fields that update updates to become externally stored
1407
		fields. Store the info to ext_vect: */
1408
1409
		ext_vect = mem_alloc(sizeof(ulint)
1410
				     * rec_offs_n_fields(offsets));
1411
		n_ext_vect = btr_push_update_extern_fields(ext_vect, offsets,
1412
							   update);
1413
		entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec,
1414
					       heap);
1415
		row_upd_index_replace_new_col_vals(entry, index, update, heap);
1416
1417
		buf = mem_heap_alloc(heap,
1418
				     rec_get_converted_size(index, entry));
1419
1420
		*old_vers = rec_convert_dtuple_to_rec(buf, index, entry);
1421
1422
		/* Now set the extern bits in the old version of the record */
1423
		rec_set_field_extern_bits(*old_vers, index,
1424
					  ext_vect, n_ext_vect, NULL);
1425
		mem_free(ext_vect);
1426
	} else {
1427
		buf = mem_heap_alloc(heap, rec_offs_size(offsets));
1428
		*old_vers = rec_copy(buf, rec, offsets);
1429
		rec_offs_make_valid(*old_vers, index, offsets);
1430
		row_upd_rec_in_place(*old_vers, offsets, update);
1431
	}
1432
1433
	return(DB_SUCCESS);
1434
}