~drizzle-trunk/drizzle/development

641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
1
/*****************************************************************************
2
1999.6.1 by kalebral at gmail
update Copyright strings to a more common format to help with creating the master debian copyright file
3
Copyright (C) 1997, 2010, Innobase Oy. All Rights Reserved.
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
4
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
8
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13
You should have received a copy of the GNU General Public License along with
1802.10.2 by Monty Taylor
Update all of the copyright headers to include the correct address.
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
16
17
*****************************************************************************/
18
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
19
/**************************************************//**
20
@file row/row0umod.c
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
21
Undo modify of a row
22
23
Created 2/27/1997 Heikki Tuuri
24
*******************************************************/
25
26
#include "row0umod.h"
27
28
#ifdef UNIV_NONINL
29
#include "row0umod.ic"
30
#endif
31
32
#include "dict0dict.h"
33
#include "dict0boot.h"
34
#include "trx0undo.h"
35
#include "trx0roll.h"
36
#include "btr0btr.h"
37
#include "mach0data.h"
38
#include "row0undo.h"
39
#include "row0vers.h"
40
#include "trx0trx.h"
41
#include "trx0rec.h"
42
#include "row0row.h"
43
#include "row0upd.h"
44
#include "que0que.h"
45
#include "log0log.h"
46
47
/* Considerations on undoing a modify operation.
48
(1) Undoing a delete marking: all index records should be found. Some of
49
them may have delete mark already FALSE, if the delete mark operation was
50
stopped underway, or if the undo operation ended prematurely because of a
51
system crash.
52
(2) Undoing an update of a delete unmarked record: the newer version of
53
an updated secondary index entry should be removed if no prior version
54
of the clustered index record requires its existence. Otherwise, it should
55
be delete marked.
56
(3) Undoing an update of a delete marked record. In this kind of update a
57
delete marked clustered index record was delete unmarked and possibly also
58
some of its fields were changed. Now, it is possible that the delete marked
59
version has become obsolete at the time the undo is started. */
60
1819.9.13 by Inaam Rana
Merge Revision revid:inaam.rana@oracle.com-20100610135811-1jxs81q050wpabyc from MySQL InnoDB
61
/*************************************************************************
1819.9.12 by Inaam Rana
Merge Revision revid:inaam.rana@oracle.com-20100608181408-c5xsjlom5olr4mxt from MySQL InnoDB
62
IMPORTANT NOTE: Any operation that generates redo MUST check that there
63
is enough space in the redo log before for that operation. This is
64
done by calling log_free_check(). The reason for checking the
65
availability of the redo log space before the start of the operation is
66
that we MUST not hold any synchonization objects when performing the
67
check.
68
If you make a change in this module make sure that no codepath is
69
introduced where a call to log_free_check() is bypassed. */
70
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
71
/***********************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
72
Checks if also the previous version of the clustered index record was
73
modified or inserted by the same transaction, and its undo number is such
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
74
that it should be undone in the same rollback.
75
@return	TRUE if also previous modify or insert of this row should be undone */
1819.9.12 by Inaam Rana
Merge Revision revid:inaam.rana@oracle.com-20100608181408-c5xsjlom5olr4mxt from MySQL InnoDB
76
static
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
77
ibool
78
row_undo_mod_undo_also_prev_vers(
79
/*=============================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
80
	undo_node_t*	node,	/*!< in: row undo node */
81
	undo_no_t*	undo_no)/*!< out: the undo number */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
82
{
83
	trx_undo_rec_t*	undo_rec;
84
	trx_t*		trx;
85
86
	trx = node->trx;
87
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
88
	if (node->new_trx_id != trx->id) {
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
89
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
90
		*undo_no = 0;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
91
		return(FALSE);
92
	}
93
94
	undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
95
96
	*undo_no = trx_undo_rec_get_undo_no(undo_rec);
97
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
98
	return(trx->roll_limit <= *undo_no);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
99
}
100
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
101
/***********************************************************//**
102
Undoes a modify in a clustered index record.
103
@return	DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
104
static
105
ulint
106
row_undo_mod_clust_low(
107
/*===================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
108
	undo_node_t*	node,	/*!< in: row undo node */
109
	que_thr_t*	thr,	/*!< in: query thread */
110
	mtr_t*		mtr,	/*!< in: mtr; must be committed before
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
111
				latching any further pages */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
112
	ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
113
{
114
	btr_pcur_t*	pcur;
115
	btr_cur_t*	btr_cur;
116
	ulint		err;
1819.9.105 by Vasil Dimov
Merge Revision revid:vasil.dimov@oracle.com-20100914110936-n60pbh1m3x0o1e5q from MySQL InnoDB
117
#ifdef UNIV_DEBUG
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
118
	ibool		success;
1819.9.105 by Vasil Dimov
Merge Revision revid:vasil.dimov@oracle.com-20100914110936-n60pbh1m3x0o1e5q from MySQL InnoDB
119
#endif /* UNIV_DEBUG */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
120
121
	pcur = &(node->pcur);
122
	btr_cur = btr_pcur_get_btr_cur(pcur);
123
1819.9.105 by Vasil Dimov
Merge Revision revid:vasil.dimov@oracle.com-20100914110936-n60pbh1m3x0o1e5q from MySQL InnoDB
124
#ifdef UNIV_DEBUG
125
	success =
126
#endif /* UNIV_DEBUG */
127
	btr_pcur_restore_position(mode, pcur, mtr);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
128
129
	ut_ad(success);
130
131
	if (mode == BTR_MODIFY_LEAF) {
132
133
		err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
134
						| BTR_NO_UNDO_LOG_FLAG
135
						| BTR_KEEP_SYS_FLAG,
136
						btr_cur, node->update,
137
						node->cmpl_info, thr, mtr);
138
	} else {
139
		mem_heap_t*	heap		= NULL;
140
		big_rec_t*	dummy_big_rec;
141
142
		ut_ad(mode == BTR_MODIFY_TREE);
143
144
		err = btr_cur_pessimistic_update(
145
			BTR_NO_LOCKING_FLAG
146
			| BTR_NO_UNDO_LOG_FLAG
147
			| BTR_KEEP_SYS_FLAG,
148
			btr_cur, &heap, &dummy_big_rec, node->update,
149
			node->cmpl_info, thr, mtr);
150
151
		ut_a(!dummy_big_rec);
152
		if (UNIV_LIKELY_NULL(heap)) {
153
			mem_heap_free(heap);
154
		}
155
	}
156
157
	return(err);
158
}
159
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
160
/***********************************************************//**
161
Removes a clustered index record after undo if possible.
1819.5.203 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6660 from MySQL InnoDB
162
This is attempted when the record was inserted by updating a
163
delete-marked record and there no longer exist transactions
164
that would see the delete-marked record.  In other words, we
165
roll back the insert by purging the record.
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
166
@return	DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
167
static
168
ulint
169
row_undo_mod_remove_clust_low(
170
/*==========================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
171
	undo_node_t*	node,	/*!< in: row undo node */
1819.5.205 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6673 from MySQL InnoDB
172
	que_thr_t*	thr,	/*!< in: query thread */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
173
	mtr_t*		mtr,	/*!< in: mtr */
174
	ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
175
{
176
	btr_pcur_t*	pcur;
177
	btr_cur_t*	btr_cur;
178
	ulint		err;
179
	ibool		success;
180
1819.5.203 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6660 from MySQL InnoDB
181
	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
182
	pcur = &(node->pcur);
183
	btr_cur = btr_pcur_get_btr_cur(pcur);
184
185
	success = btr_pcur_restore_position(mode, pcur, mtr);
186
187
	if (!success) {
188
189
		return(DB_SUCCESS);
190
	}
191
192
	/* Find out if we can remove the whole clustered index record */
193
194
	if (node->rec_type == TRX_UNDO_UPD_DEL_REC
195
	    && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
196
197
		/* Ok, we can remove */
198
	} else {
199
		return(DB_SUCCESS);
200
	}
201
202
	if (mode == BTR_MODIFY_LEAF) {
203
		success = btr_cur_optimistic_delete(btr_cur, mtr);
204
205
		if (success) {
206
			err = DB_SUCCESS;
207
		} else {
208
			err = DB_FAIL;
209
		}
210
	} else {
211
		ut_ad(mode == BTR_MODIFY_TREE);
212
1819.5.205 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6673 from MySQL InnoDB
213
		/* This operation is analogous to purge, we can free also
214
		inherited externally stored fields */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
215
1819.5.205 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6673 from MySQL InnoDB
216
		btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
217
					   thr_is_recv(thr)
218
					   ? RB_RECOVERY_PURGE_REC
219
					   : RB_NONE, mtr);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
220
221
		/* The delete operation may fail if we have little
222
		file space left: TODO: easiest to crash the database
223
		and restart with more file space */
224
	}
225
226
	return(err);
227
}
228
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
229
/***********************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
230
Undoes a modify in a clustered index record. Sets also the node state for the
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
231
next round of undo.
232
@return	DB_SUCCESS or error code: we may run out of file space */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
233
static
234
ulint
235
row_undo_mod_clust(
236
/*===============*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
237
	undo_node_t*	node,	/*!< in: row undo node */
238
	que_thr_t*	thr)	/*!< in: query thread */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
239
{
240
	btr_pcur_t*	pcur;
241
	mtr_t		mtr;
242
	ulint		err;
243
	ibool		success;
244
	ibool		more_vers;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
245
	undo_no_t	new_undo_no;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
246
247
	ut_ad(node && thr);
248
1819.9.12 by Inaam Rana
Merge Revision revid:inaam.rana@oracle.com-20100608181408-c5xsjlom5olr4mxt from MySQL InnoDB
249
	log_free_check();
250
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
251
	/* Check if also the previous version of the clustered index record
252
	should be undone in this same rollback operation */
253
254
	more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
255
256
	pcur = &(node->pcur);
257
258
	mtr_start(&mtr);
259
260
	/* Try optimistic processing of the record, keeping changes within
261
	the index page */
262
263
	err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
264
265
	if (err != DB_SUCCESS) {
266
		btr_pcur_commit_specify_mtr(pcur, &mtr);
267
268
		/* We may have to modify tree structure: do a pessimistic
269
		descent down the index tree */
270
271
		mtr_start(&mtr);
272
273
		err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
274
	}
275
276
	btr_pcur_commit_specify_mtr(pcur, &mtr);
277
278
	if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
279
280
		mtr_start(&mtr);
281
282
		err = row_undo_mod_remove_clust_low(node, thr, &mtr,
283
						    BTR_MODIFY_LEAF);
284
		if (err != DB_SUCCESS) {
285
			btr_pcur_commit_specify_mtr(pcur, &mtr);
286
287
			/* We may have to modify tree structure: do a
288
			pessimistic descent down the index tree */
289
290
			mtr_start(&mtr);
291
292
			err = row_undo_mod_remove_clust_low(node, thr, &mtr,
293
							    BTR_MODIFY_TREE);
294
		}
295
296
		btr_pcur_commit_specify_mtr(pcur, &mtr);
297
	}
298
299
	node->state = UNDO_NODE_FETCH_NEXT;
300
301
	trx_undo_rec_release(node->trx, node->undo_no);
302
303
	if (more_vers && err == DB_SUCCESS) {
304
305
		/* Reserve the undo log record to the prior version after
306
		committing &mtr: this is necessary to comply with the latching
307
		order, as &mtr may contain the fsp latch which is lower in
308
		the latch hierarchy than trx->undo_mutex. */
309
310
		success = trx_undo_rec_reserve(node->trx, new_undo_no);
311
312
		if (success) {
313
			node->state = UNDO_NODE_PREV_VERS;
314
		}
315
	}
316
317
	return(err);
318
}
319
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
320
/***********************************************************//**
321
Delete marks or removes a secondary index entry if found.
322
@return	DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
323
static
324
ulint
325
row_undo_mod_del_mark_or_remove_sec_low(
326
/*====================================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
327
	undo_node_t*	node,	/*!< in: row undo node */
328
	que_thr_t*	thr,	/*!< in: query thread */
329
	dict_index_t*	index,	/*!< in: index */
330
	dtuple_t*	entry,	/*!< in: index entry */
331
	ulint		mode)	/*!< in: latch mode BTR_MODIFY_LEAF or
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
332
				BTR_MODIFY_TREE */
333
{
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
334
	btr_pcur_t		pcur;
335
	btr_cur_t*		btr_cur;
336
	ibool			success;
337
	ibool			old_has;
338
	ulint			err;
339
	mtr_t			mtr;
340
	mtr_t			mtr_vers;
341
	enum row_search_result	search_result;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
342
343
	log_free_check();
344
	mtr_start(&mtr);
345
346
	btr_cur = btr_pcur_get_btr_cur(&pcur);
347
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
348
	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
349
350
	search_result = row_search_index_entry(index, entry, mode,
351
					       &pcur, &mtr);
352
353
	switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
354
	case ROW_NOT_FOUND:
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
355
		/* In crash recovery, the secondary index record may
356
		be missing if the UPDATE did not have time to insert
357
		the secondary index records before the crash.  When we
358
		are undoing that UPDATE in crash recovery, the record
359
		may be missing.
360
361
		In normal processing, if an update ends in a deadlock
362
		before it has inserted all updated secondary index
363
		records, then the undo will not find those records. */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
364
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
365
		err = DB_SUCCESS;
366
		goto func_exit;
367
	case ROW_FOUND:
368
		break;
369
	case ROW_BUFFERED:
370
	case ROW_NOT_DELETED_REF:
371
		/* These are invalid outcomes, because the mode passed
372
		to row_search_index_entry() did not include any of the
373
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
374
		ut_error;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
375
	}
376
377
	/* We should remove the index record if no prior version of the row,
378
	which cannot be purged yet, requires its existence. If some requires,
379
	we should delete mark the record. */
380
381
	mtr_start(&mtr_vers);
382
383
	success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
384
					    &mtr_vers);
385
	ut_a(success);
386
387
	old_has = row_vers_old_has_index_entry(FALSE,
388
					       btr_pcur_get_rec(&(node->pcur)),
389
					       &mtr_vers, index, entry);
390
	if (old_has) {
391
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
392
						   btr_cur, TRUE, thr, &mtr);
393
		ut_ad(err == DB_SUCCESS);
394
	} else {
395
		/* Remove the index record */
396
397
		if (mode == BTR_MODIFY_LEAF) {
398
			success = btr_cur_optimistic_delete(btr_cur, &mtr);
399
			if (success) {
400
				err = DB_SUCCESS;
401
			} else {
402
				err = DB_FAIL;
403
			}
404
		} else {
405
			ut_ad(mode == BTR_MODIFY_TREE);
406
1819.5.205 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6673 from MySQL InnoDB
407
			/* No need to distinguish RB_RECOVERY_PURGE here,
408
			because we are deleting a secondary index record:
409
			the distinction between RB_NORMAL and
410
			RB_RECOVERY_PURGE only matters when deleting a
411
			record that contains externally stored
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
412
			columns. */
413
			ut_ad(!dict_index_is_clust(index));
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
414
			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
415
						   RB_NORMAL, &mtr);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
416
417
			/* The delete operation may fail if we have little
418
			file space left: TODO: easiest to crash the database
419
			and restart with more file space */
420
		}
421
	}
422
423
	btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
424
425
func_exit:
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
426
	btr_pcur_close(&pcur);
427
	mtr_commit(&mtr);
428
429
	return(err);
430
}
431
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
432
/***********************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
433
Delete marks or removes a secondary index entry if found.
434
NOTE that if we updated the fields of a delete-marked secondary index record
435
so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
436
return to the original values because we do not know them. But this should
437
not cause problems because in row0sel.c, in queries we always retrieve the
438
clustered index record or an earlier version of it, if the secondary index
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
439
record through which we do the search is delete-marked.
440
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
441
static
442
ulint
443
row_undo_mod_del_mark_or_remove_sec(
444
/*================================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
445
	undo_node_t*	node,	/*!< in: row undo node */
446
	que_thr_t*	thr,	/*!< in: query thread */
447
	dict_index_t*	index,	/*!< in: index */
448
	dtuple_t*	entry)	/*!< in: index entry */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
449
{
450
	ulint	err;
451
452
	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
453
						      entry, BTR_MODIFY_LEAF);
454
	if (err == DB_SUCCESS) {
455
456
		return(err);
457
	}
458
459
	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
460
						      entry, BTR_MODIFY_TREE);
461
	return(err);
462
}
463
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
464
/***********************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
465
Delete unmarks a secondary index entry which must be found. It might not be
466
delete-marked at the moment, but it does not harm to unmark it anyway. We also
467
need to update the fields of the secondary index record if we updated its
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
468
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
469
@return	DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
470
static
471
ulint
472
row_undo_mod_del_unmark_sec_and_undo_update(
473
/*========================================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
474
	ulint		mode,	/*!< in: search mode: BTR_MODIFY_LEAF or
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
475
				BTR_MODIFY_TREE */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
476
	que_thr_t*	thr,	/*!< in: query thread */
477
	dict_index_t*	index,	/*!< in: index */
1819.5.153 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6305 from MySQL InnoDB
478
	const dtuple_t*	entry)	/*!< in: index entry */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
479
{
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
480
	mem_heap_t*		heap;
481
	btr_pcur_t		pcur;
482
	btr_cur_t*		btr_cur;
483
	upd_t*			update;
484
	ulint			err		= DB_SUCCESS;
485
	big_rec_t*		dummy_big_rec;
486
	mtr_t			mtr;
487
	trx_t*			trx		= thr_get_trx(thr);
488
	enum row_search_result	search_result;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
489
490
	/* Ignore indexes that are being created. */
491
	if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
492
493
		return(DB_SUCCESS);
494
	}
495
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
496
	log_free_check();
497
	mtr_start(&mtr);
498
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
499
	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
500
501
	search_result = row_search_index_entry(index, entry, mode,
502
					       &pcur, &mtr);
503
504
	switch (search_result) {
505
	case ROW_BUFFERED:
506
	case ROW_NOT_DELETED_REF:
507
		/* These are invalid outcomes, because the mode passed
508
		to row_search_index_entry() did not include any of the
509
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
510
		ut_error;
511
	case ROW_NOT_FOUND:
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
512
		fputs("InnoDB: error in sec index entry del undo in\n"
513
		      "InnoDB: ", stderr);
514
		dict_index_name_print(stderr, trx, index);
515
		fputs("\n"
516
		      "InnoDB: tuple ", stderr);
517
		dtuple_print(stderr, entry);
518
		fputs("\n"
519
		      "InnoDB: record ", stderr);
520
		rec_print(stderr, btr_pcur_get_rec(&pcur), index);
521
		putc('\n', stderr);
522
		trx_print(stderr, trx, 0);
523
		fputs("\n"
524
		      "InnoDB: Submit a detailed bug report"
525
		      " to http://bugs.mysql.com\n", stderr);
1819.7.68 by Stewart Smith
Merge initial InnoDB+ import.
526
		break;
527
	case ROW_FOUND:
528
		btr_cur = btr_pcur_get_btr_cur(&pcur);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
529
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
530
						   btr_cur, FALSE, thr, &mtr);
531
		ut_a(err == DB_SUCCESS);
532
		heap = mem_heap_create(100);
533
534
		update = row_upd_build_sec_rec_difference_binary(
535
			index, entry, btr_cur_get_rec(btr_cur), trx, heap);
536
		if (upd_get_n_fields(update) == 0) {
537
538
			/* Do nothing */
539
540
		} else if (mode == BTR_MODIFY_LEAF) {
541
			/* Try an optimistic updating of the record, keeping
542
			changes within the page */
543
544
			err = btr_cur_optimistic_update(
545
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
546
				btr_cur, update, 0, thr, &mtr);
547
			switch (err) {
548
			case DB_OVERFLOW:
549
			case DB_UNDERFLOW:
550
			case DB_ZIP_OVERFLOW:
551
				err = DB_FAIL;
552
			}
553
		} else {
554
			ut_a(mode == BTR_MODIFY_TREE);
555
			err = btr_cur_pessimistic_update(
556
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
557
				btr_cur, &heap, &dummy_big_rec,
558
				update, 0, thr, &mtr);
559
			ut_a(!dummy_big_rec);
560
		}
561
562
		mem_heap_free(heap);
563
	}
564
565
	btr_pcur_close(&pcur);
566
	mtr_commit(&mtr);
567
568
	return(err);
569
}
570
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
571
/***********************************************************//**
572
Undoes a modify in secondary indexes when undo record type is UPD_DEL.
573
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
574
static
575
ulint
576
row_undo_mod_upd_del_sec(
577
/*=====================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
578
	undo_node_t*	node,	/*!< in: row undo node */
579
	que_thr_t*	thr)	/*!< in: query thread */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
580
{
581
	mem_heap_t*	heap;
582
	dtuple_t*	entry;
583
	dict_index_t*	index;
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
584
	ulint		err	= DB_SUCCESS;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
585
1819.5.203 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6660 from MySQL InnoDB
586
	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
587
	heap = mem_heap_create(1024);
588
589
	while (node->index != NULL) {
590
		index = node->index;
591
592
		entry = row_build_index_entry(node->row, node->ext,
593
					      index, heap);
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
594
		if (UNIV_UNLIKELY(!entry)) {
595
			/* The database must have crashed after
596
			inserting a clustered index record but before
597
			writing all the externally stored columns of
598
			that record.  Because secondary index entries
599
			are inserted after the clustered index record,
600
			we may assume that the secondary index record
601
			does not exist.  However, this situation may
602
			only occur during the rollback of incomplete
603
			transactions. */
1819.5.204 by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6672 from MySQL InnoDB
604
			ut_a(thr_is_recv(thr));
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
605
		} else {
606
			err = row_undo_mod_del_mark_or_remove_sec(
607
				node, thr, index, entry);
608
609
			if (err != DB_SUCCESS) {
610
611
				break;
612
			}
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
613
		}
614
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
615
		mem_heap_empty(heap);
616
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
617
		node->index = dict_table_get_next_index(node->index);
618
	}
619
620
	mem_heap_free(heap);
621
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
622
	return(err);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
623
}
624
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
625
/***********************************************************//**
626
Undoes a modify in secondary indexes when undo record type is DEL_MARK.
627
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
628
static
629
ulint
630
row_undo_mod_del_mark_sec(
631
/*======================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
632
	undo_node_t*	node,	/*!< in: row undo node */
633
	que_thr_t*	thr)	/*!< in: query thread */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
634
{
635
	mem_heap_t*	heap;
636
	dtuple_t*	entry;
637
	dict_index_t*	index;
638
	ulint		err;
639
640
	heap = mem_heap_create(1024);
641
642
	while (node->index != NULL) {
643
		index = node->index;
644
645
		entry = row_build_index_entry(node->row, node->ext,
646
					      index, heap);
647
		ut_a(entry);
648
		err = row_undo_mod_del_unmark_sec_and_undo_update(
649
			BTR_MODIFY_LEAF, thr, index, entry);
650
		if (err == DB_FAIL) {
651
			err = row_undo_mod_del_unmark_sec_and_undo_update(
652
				BTR_MODIFY_TREE, thr, index, entry);
653
		}
654
655
		if (err != DB_SUCCESS) {
656
657
			mem_heap_free(heap);
658
659
			return(err);
660
		}
661
662
		node->index = dict_table_get_next_index(node->index);
663
	}
664
665
	mem_heap_free(heap);
666
667
	return(DB_SUCCESS);
668
}
669
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
670
/***********************************************************//**
671
Undoes a modify in secondary indexes when undo record type is UPD_EXIST.
672
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
673
static
674
ulint
675
row_undo_mod_upd_exist_sec(
676
/*=======================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
677
	undo_node_t*	node,	/*!< in: row undo node */
678
	que_thr_t*	thr)	/*!< in: query thread */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
679
{
680
	mem_heap_t*	heap;
681
	dtuple_t*	entry;
682
	dict_index_t*	index;
683
	ulint		err;
684
685
	if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
686
		/* No change in secondary indexes */
687
688
		return(DB_SUCCESS);
689
	}
690
691
	heap = mem_heap_create(1024);
692
693
	while (node->index != NULL) {
694
		index = node->index;
695
696
		if (row_upd_changes_ord_field_binary(node->row, node->index,
697
						     node->update)) {
698
699
			/* Build the newest version of the index entry */
700
			entry = row_build_index_entry(node->row, node->ext,
701
						      index, heap);
1819.7.153 by Marko Mäkelä
Merge Revision revid:marko.makela@oracle.com-20100601120501-rw667o3up7hh750p from MySQL InnoDB
702
			if (UNIV_UNLIKELY(!entry)) {
703
				/* The server must have crashed in
704
				row_upd_clust_rec_by_insert(), in
705
				row_ins_index_entry_low() before
706
				btr_store_big_rec_extern_fields()
707
				has written the externally stored columns
708
				(BLOBs) of the new clustered index entry. */
709
710
				/* The table must be in DYNAMIC or COMPRESSED
711
				format.  REDUNDANT and COMPACT formats
712
				store a local 768-byte prefix of each
713
				externally stored column. */
714
				ut_a(dict_table_get_format(index->table)
715
				     >= DICT_TF_FORMAT_ZIP);
716
717
				/* This is only legitimate when
718
				rolling back an incomplete transaction
719
				after crash recovery. */
720
				ut_a(thr_get_trx(thr)->is_recovered);
721
722
				/* The server must have crashed before
723
				completing the insert of the new
724
				clustered index entry and before
725
				inserting to the secondary indexes.
726
				Because node->row was not yet written
727
				to this index, we can ignore it.  But
728
				we must restore node->undo_row. */
729
			} else {
730
				/* NOTE that if we updated the fields of a
731
				delete-marked secondary index record so that
732
				alphabetically they stayed the same, e.g.,
733
				'abc' -> 'aBc', we cannot return to the
734
				original values because we do not know them.
735
				But this should not cause problems because
736
				in row0sel.c, in queries we always retrieve
737
				the clustered index record or an earlier
738
				version of it, if the secondary index record
739
				through which we do the search is
740
				delete-marked. */
741
742
				err = row_undo_mod_del_mark_or_remove_sec(
743
					node, thr, index, entry);
744
				if (err != DB_SUCCESS) {
745
					mem_heap_free(heap);
746
747
					return(err);
748
				}
749
750
				mem_heap_empty(heap);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
751
			}
752
753
			/* We may have to update the delete mark in the
754
			secondary index record of the previous version of
755
			the row. We also need to update the fields of
756
			the secondary index record if we updated its fields
757
			but alphabetically they stayed the same, e.g.,
758
			'abc' -> 'aBc'. */
759
			entry = row_build_index_entry(node->undo_row,
760
						      node->undo_ext,
761
						      index, heap);
762
			ut_a(entry);
763
764
			err = row_undo_mod_del_unmark_sec_and_undo_update(
765
				BTR_MODIFY_LEAF, thr, index, entry);
766
			if (err == DB_FAIL) {
767
				err = row_undo_mod_del_unmark_sec_and_undo_update(
768
					BTR_MODIFY_TREE, thr, index, entry);
769
			}
770
771
			if (err != DB_SUCCESS) {
772
				mem_heap_free(heap);
773
774
				return(err);
775
			}
776
		}
777
778
		node->index = dict_table_get_next_index(node->index);
779
	}
780
781
	mem_heap_free(heap);
782
783
	return(DB_SUCCESS);
784
}
785
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
786
/***********************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
787
Parses the row reference and other info in a modify undo log record. */
788
static
789
void
790
row_undo_mod_parse_undo_rec(
791
/*========================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
792
	undo_node_t*	node,	/*!< in: row undo node */
793
	que_thr_t*	thr)	/*!< in: query thread */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
794
{
795
	dict_index_t*	clust_index;
796
	byte*		ptr;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
797
	undo_no_t	undo_no;
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
798
	table_id_t	table_id;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
799
	trx_id_t	trx_id;
800
	roll_ptr_t	roll_ptr;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
801
	ulint		info_bits;
802
	ulint		type;
803
	ulint		cmpl_info;
804
	ibool		dummy_extern;
805
	trx_t*		trx;
806
807
	ut_ad(node && thr);
808
	trx = thr_get_trx(thr);
809
	ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
810
				    &dummy_extern, &undo_no, &table_id);
811
	node->rec_type = type;
812
813
	node->table = dict_table_get_on_id(table_id, trx);
814
815
	/* TODO: other fixes associated with DROP TABLE + rollback in the
816
	same table by another user */
817
818
	if (node->table == NULL) {
819
		/* Table was dropped */
820
		return;
821
	}
822
823
	if (node->table->ibd_file_missing) {
824
		/* We skip undo operations to missing .ibd files */
825
		node->table = NULL;
826
827
		return;
828
	}
829
830
	clust_index = dict_table_get_first_index(node->table);
831
832
	ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
833
					       &info_bits);
834
835
	ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
836
				       node->heap);
837
838
	trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
839
				       roll_ptr, info_bits, trx,
840
				       node->heap, &(node->update));
841
	node->new_roll_ptr = roll_ptr;
842
	node->new_trx_id = trx_id;
843
	node->cmpl_info = cmpl_info;
844
}
845
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
846
/***********************************************************//**
847
Undoes a modify operation on a row of a table.
848
@return	DB_SUCCESS or error code */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
849
UNIV_INTERN
850
ulint
851
row_undo_mod(
852
/*=========*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
853
	undo_node_t*	node,	/*!< in: row undo node */
854
	que_thr_t*	thr)	/*!< in: query thread */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
855
{
856
	ulint	err;
857
858
	ut_ad(node && thr);
859
	ut_ad(node->state == UNDO_NODE_MODIFY);
860
861
	row_undo_mod_parse_undo_rec(node, thr);
862
863
	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
864
		/* It is already undone, or will be undone by another query
865
		thread, or table was dropped */
866
867
		trx_undo_rec_release(node->trx, node->undo_no);
868
		node->state = UNDO_NODE_FETCH_NEXT;
869
870
		return(DB_SUCCESS);
871
	}
872
873
	node->index = dict_table_get_next_index(
874
		dict_table_get_first_index(node->table));
875
876
	if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
877
878
		err = row_undo_mod_upd_exist_sec(node, thr);
879
880
	} else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
881
882
		err = row_undo_mod_del_mark_sec(node, thr);
883
	} else {
884
		ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
885
		err = row_undo_mod_upd_del_sec(node, thr);
886
	}
887
888
	if (err != DB_SUCCESS) {
889
890
		return(err);
891
	}
892
893
	err = row_undo_mod_clust(node, thr);
894
895
	return(err);
896
}