1
/*****************************************************************************
3
Copyright (C) 1997, 2010, Innobase Oy. All Rights Reserved.
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.
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.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
17
*****************************************************************************/
19
/**************************************************//**
1
/******************************************************
21
2
Purge obsolete records
23
6
Created 3/14/1997 Heikki Tuuri
24
7
*******************************************************/
44
27
#include "row0mysql.h"
45
28
#include "log0log.h"
47
/*************************************************************************
48
IMPORTANT NOTE: Any operation that generates redo MUST check that there
49
is enough space in the redo log before for that operation. This is
50
done by calling log_free_check(). The reason for checking the
51
availability of the redo log space before the start of the operation is
52
that we MUST not hold any synchonization objects when performing the
54
If you make a change in this module make sure that no codepath is
55
introduced where a call to log_free_check() is bypassed. */
57
/*************************************************************************
58
IMPORTANT NOTE: Any operation that generates redo MUST check that there
59
is enough space in the redo log before for that operation. This is
60
done by calling log_free_check(). The reason for checking the
61
availability of the redo log space before the start of the operation is
62
that we MUST not hold any synchonization objects when performing the
64
If you make a change in this module make sure that no codepath is
65
introduced where a call to log_free_check() is bypassed. */
67
/********************************************************************//**
68
Creates a purge node to a query graph.
69
@return own: purge node */
30
/************************************************************************
31
Creates a purge node to a query graph. */
72
34
row_purge_node_create(
73
35
/*==================*/
74
que_thr_t* parent, /*!< in: parent node, i.e., a thr node */
75
mem_heap_t* heap) /*!< in: memory heap where created */
36
/* out, own: purge node */
37
que_thr_t* parent, /* in: parent node, i.e., a thr node */
38
mem_heap_t* heap) /* in: memory heap where created */
77
40
purge_node_t* node;
79
42
ut_ad(parent && heap);
81
node = static_cast<purge_node_t *>(mem_heap_alloc(heap, sizeof(purge_node_t)));
44
node = mem_heap_alloc(heap, sizeof(purge_node_t));
83
46
node->common.type = QUE_NODE_PURGE;
84
47
node->common.parent = parent;
91
/***********************************************************//**
54
/***************************************************************
92
55
Repositions the pcur in the purge node on the clustered index record,
94
@return TRUE if the record was found */
97
59
row_purge_reposition_pcur(
98
60
/*======================*/
99
ulint mode, /*!< in: latching mode */
100
purge_node_t* node, /*!< in: row purge node */
101
mtr_t* mtr) /*!< in: mtr */
61
/* out: TRUE if the record was found */
62
ulint mode, /* in: latching mode */
63
purge_node_t* node, /* in: row purge node */
64
mtr_t* mtr) /* in: mtr */
122
/***********************************************************//**
123
Removes a delete marked clustered index record if possible.
124
@return TRUE if success, or if not found, or if modified after the
85
/***************************************************************
86
Removes a delete marked clustered index record if possible. */
128
89
row_purge_remove_clust_if_poss_low(
129
90
/*===============================*/
130
purge_node_t* node, /*!< in: row purge node */
131
ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
91
/* out: TRUE if success, or if not found, or
92
if modified after the delete marking */
93
purge_node_t* node, /* in: row purge node */
94
ulint mode) /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
133
96
dict_index_t* index;
162
124
rec = btr_pcur_get_rec(pcur);
164
if (node->roll_ptr != row_get_rec_roll_ptr(
165
rec, index, rec_get_offsets(rec, index, offsets_,
166
ULINT_UNDEFINED, &heap))) {
126
if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(
127
rec, index, rec_get_offsets(
128
rec, index, offsets_,
129
ULINT_UNDEFINED, &heap)))) {
167
130
if (UNIV_LIKELY_NULL(heap)) {
168
131
mem_heap_free(heap);
237
/***********************************************************//**
238
Determines if it is possible to remove a secondary index entry.
239
Removal is possible if the secondary index entry does not refer to any
240
not delete marked version of a clustered index record where DB_TRX_ID
241
is newer than the purge view.
243
NOTE: This function should only be called by the purge thread, only
244
while holding a latch on the leaf page of the secondary index entry
245
(or keeping the buffer pool watch on the page). It is possible that
246
this function first returns TRUE and then FALSE, if a user transaction
247
inserts a record that the secondary index entry would refer to.
248
However, in that case, the user transaction would also re-insert the
249
secondary index entry after purge has removed it and released the leaf
251
@return TRUE if the secondary index record can be purged */
256
purge_node_t* node, /*!< in/out: row purge node */
257
dict_index_t* index, /*!< in: secondary index */
258
const dtuple_t* entry) /*!< in: secondary index entry */
263
ut_ad(!dict_index_is_clust(index));
266
can_delete = !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr)
267
|| !row_vers_old_has_index_entry(TRUE,
268
btr_pcur_get_rec(&node->pcur),
271
btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
276
200
/***************************************************************
277
Removes a secondary index entry if possible, by modifying the
278
index tree. Does not try to buffer the delete.
279
@return TRUE if success or if not found */
201
Removes a secondary index entry if possible. */
282
row_purge_remove_sec_if_poss_tree(
283
/*==============================*/
284
purge_node_t* node, /*!< in: row purge node */
285
dict_index_t* index, /*!< in: index */
286
const dtuple_t* entry) /*!< in: index entry */
204
row_purge_remove_sec_if_poss_low(
205
/*=============================*/
206
/* out: TRUE if success or if not found */
207
purge_node_t* node, /* in: row purge node */
208
dict_index_t* index, /* in: index */
209
const dtuple_t* entry, /* in: index entry */
210
ulint mode) /* in: latch mode BTR_MODIFY_LEAF or
290
ibool success = TRUE;
293
enum row_search_result search_result;
216
ibool old_has = 0; /* remove warning */
295
222
log_free_check();
298
search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE,
225
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
301
switch (search_result) {
303
/* Not found. This is a legitimate condition. In a
304
rollback, InnoDB will remove secondary recs that would
305
be purged anyway. Then the actual purge will not find
306
the secondary index record. Also, the purge itself is
307
eager: if it comes to consider a secondary index
308
record, and notices it does not need to exist in the
309
index, it will remove it. Then if/when the purge
310
comes to consider the secondary index record a second
311
time, it will not exist any more in the index. */
313
230
/* fputs("PURGE:........sec entry not found\n", stderr); */
314
231
/* dtuple_print(stderr, entry); */
319
case ROW_NOT_DELETED_REF:
320
/* These are invalid outcomes, because the mode passed
321
to row_search_index_entry() did not include any of the
322
flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
233
btr_pcur_close(&pcur);
326
239
btr_cur = btr_pcur_get_btr_cur(&pcur);
329
242
which cannot be purged yet, requires its existence. If some requires,
330
243
we should do nothing. */
332
if (row_purge_poss_sec(node, index, entry)) {
333
/* Remove the index record, which should have been
334
marked for deletion. */
335
ut_ad(REC_INFO_DELETED_FLAG
336
& rec_get_info_bits(btr_cur_get_rec(btr_cur),
337
dict_table_is_comp(index->table)));
339
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
341
switch (UNIV_EXPECT(err, DB_SUCCESS)) {
344
case DB_OUT_OF_FILE_SPACE:
245
mtr_vers = mem_alloc(sizeof(mtr_t));
249
success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr_vers);
252
old_has = row_vers_old_has_index_entry(
253
TRUE, btr_pcur_get_rec(&(node->pcur)),
254
mtr_vers, index, entry);
257
btr_pcur_commit_specify_mtr(&(node->pcur), mtr_vers);
261
if (!success || !old_has) {
262
/* Remove the index record */
264
if (mode == BTR_MODIFY_LEAF) {
265
success = btr_cur_optimistic_delete(btr_cur, &mtr);
267
ut_ad(mode == BTR_MODIFY_TREE);
268
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
270
success = err == DB_SUCCESS;
271
ut_a(success || err == DB_OUT_OF_FILE_SPACE);
353
275
btr_pcur_close(&pcur);
354
276
mtr_commit(&mtr);
359
281
/***************************************************************
360
Removes a secondary index entry without modifying the index tree,
362
@return TRUE if success or if not found */
365
row_purge_remove_sec_if_poss_leaf(
366
/*==============================*/
367
purge_node_t* node, /*!< in: row purge node */
368
dict_index_t* index, /*!< in: index */
369
const dtuple_t* entry) /*!< in: index entry */
373
enum row_search_result search_result;
379
/* Set the purge node for the call to row_purge_poss_sec(). */
380
pcur.btr_cur.purge_node = node;
381
/* Set the query thread, so that ibuf_insert_low() will be
382
able to invoke thd_get_trx(). */
383
pcur.btr_cur.thr = static_cast<que_thr_t *>(que_node_get_parent(node));
385
search_result = row_search_index_entry(
386
index, entry, BTR_MODIFY_LEAF | BTR_DELETE, &pcur, &mtr);
388
switch (search_result) {
391
/* Before attempting to purge a record, check
392
if it is safe to do so. */
393
if (row_purge_poss_sec(node, index, entry)) {
394
btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
396
/* Only delete-marked records should be purged. */
397
ut_ad(REC_INFO_DELETED_FLAG
399
btr_cur_get_rec(btr_cur),
400
dict_table_is_comp(index->table)));
402
if (!btr_cur_optimistic_delete(btr_cur, &mtr)) {
404
/* The index entry could not be deleted. */
409
/* fall through (the index entry is still needed,
410
or the deletion succeeded) */
411
case ROW_NOT_DELETED_REF:
412
/* The index entry is still needed. */
414
/* The deletion was buffered. */
416
/* The index entry does not exist, nothing to do. */
419
btr_pcur_close(&pcur);
428
/***********************************************************//**
429
282
Removes a secondary index entry if possible. */
432
285
row_purge_remove_sec_if_poss(
433
286
/*=========================*/
434
purge_node_t* node, /*!< in: row purge node */
435
dict_index_t* index, /*!< in: index */
436
dtuple_t* entry) /*!< in: index entry */
287
purge_node_t* node, /* in: row purge node */
288
dict_index_t* index, /* in: index */
289
dtuple_t* entry) /* in: index entry */
439
292
ulint n_tries = 0;
441
294
/* fputs("Purge: Removing secondary record\n", stderr); */
443
if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) {
296
success = row_purge_remove_sec_if_poss_low(node, index, entry,
448
success = row_purge_remove_sec_if_poss_tree(node, index, entry);
303
success = row_purge_remove_sec_if_poss_low(node, index, entry,
449
305
/* The delete operation may fail if we have little
450
306
file space left: TODO: easiest to crash the database
451
307
and restart with more file space */
611
/***********************************************************//**
612
Parses the row reference and other info in a modify undo log record.
613
@return TRUE if purge operation required: NOTE that then the CALLER
614
must unfreeze data dictionary! */
467
/***************************************************************
468
Parses the row reference and other info in a modify undo log record. */
617
471
row_purge_parse_undo_rec(
618
472
/*=====================*/
619
purge_node_t* node, /*!< in: row undo node */
473
/* out: TRUE if purge operation required:
474
NOTE that then the CALLER must unfreeze
476
purge_node_t* node, /* in: row undo node */
620
477
ibool* updated_extern,
621
/*!< out: TRUE if an externally stored field
478
/* out: TRUE if an externally stored field
623
que_thr_t* thr) /*!< in: query thread */
480
que_thr_t* thr) /* in: query thread */
625
482
dict_index_t* clust_index;
714
/***********************************************************//**
571
/***************************************************************
715
572
Fetches an undo log record and does the purge for the recorded operation.
716
573
If none left, or the current purge completed, returns the control to the
717
parent node, which is always a query thread node.
718
@return DB_SUCCESS if operation successfully completed, else error code */
574
parent node, which is always a query thread node. */
723
purge_node_t* node, /*!< in: row purge node */
724
que_thr_t* thr) /*!< in: query thread */
579
/* out: DB_SUCCESS if operation successfully
580
completed, else error code */
581
purge_node_t* node, /* in: row purge node */
582
que_thr_t* thr) /* in: query thread */
728
ibool updated_extern;
586
ibool updated_extern;
731
589
ut_ad(node && thr);
785
643
return(DB_SUCCESS);
788
/***********************************************************//**
646
/***************************************************************
789
647
Does the purge operation for a single undo log record. This is a high-level
790
function used in an SQL execution graph.
791
@return query thread to run next or NULL */
648
function used in an SQL execution graph. */
796
que_thr_t* thr) /*!< in: query thread */
653
/* out: query thread to run next or NULL */
654
que_thr_t* thr) /* in: query thread */
798
656
purge_node_t* node;
801
#endif /* UNIV_DEBUG */
805
node = static_cast<purge_node_t *>(thr->run_node);
661
node = thr->run_node;
807
663
ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
811
#endif /* UNIV_DEBUG */
812
row_purge(node, thr);
665
err = row_purge(node, thr);
815
ut_a(err == DB_SUCCESS);
816
#endif /* UNIV_DEBUG */
667
ut_ad(err == DB_SUCCESS);