~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/row/row0purge.c

  • Committer: Brian Aker
  • Date: 2009-04-27 14:36:40 UTC
  • Revision ID: brian@gaz-20090427143640-f6zjmtt9vm55qgm2
Patch on show processlist from  davi@apache.org

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*****************************************************************************
2
2
 
3
 
Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved.
 
3
Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
4
4
 
5
5
This program is free software; you can redistribute it and/or modify it under
6
6
the terms of the GNU General Public License as published by the Free Software
11
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
12
 
13
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
 
14
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
15
Place, Suite 330, Boston, MA 02111-1307 USA
16
16
 
17
17
*****************************************************************************/
18
18
 
19
 
/**************************************************//**
20
 
@file row/row0purge.c
 
19
/******************************************************
21
20
Purge obsolete records
22
21
 
23
22
Created 3/14/1997 Heikki Tuuri
44
43
#include "row0mysql.h"
45
44
#include "log0log.h"
46
45
 
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
53
 
check.
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. */
56
 
 
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
63
 
check.
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. */
66
 
 
67
 
/********************************************************************//**
68
 
Creates a purge node to a query graph.
69
 
@return own: purge node */
 
46
/************************************************************************
 
47
Creates a purge node to a query graph. */
70
48
UNIV_INTERN
71
49
purge_node_t*
72
50
row_purge_node_create(
73
51
/*==================*/
74
 
        que_thr_t*      parent, /*!< in: parent node, i.e., a thr node */
75
 
        mem_heap_t*     heap)   /*!< in: memory heap where created */
 
52
                                /* out, own: purge node */
 
53
        que_thr_t*      parent, /* in: parent node, i.e., a thr node */
 
54
        mem_heap_t*     heap)   /* in: memory heap where created */
76
55
{
77
56
        purge_node_t*   node;
78
57
 
88
67
        return(node);
89
68
}
90
69
 
91
 
/***********************************************************//**
 
70
/***************************************************************
92
71
Repositions the pcur in the purge node on the clustered index record,
93
 
if found.
94
 
@return TRUE if the record was found */
 
72
if found. */
95
73
static
96
74
ibool
97
75
row_purge_reposition_pcur(
98
76
/*======================*/
99
 
        ulint           mode,   /*!< in: latching mode */
100
 
        purge_node_t*   node,   /*!< in: row purge node */
101
 
        mtr_t*          mtr)    /*!< in: mtr */
 
77
                                /* out: TRUE if the record was found */
 
78
        ulint           mode,   /* in: latching mode */
 
79
        purge_node_t*   node,   /* in: row purge node */
 
80
        mtr_t*          mtr)    /* in: mtr */
102
81
{
103
82
        ibool   found;
104
83
 
119
98
        return(found);
120
99
}
121
100
 
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
125
 
delete marking */
 
101
/***************************************************************
 
102
Removes a delete marked clustered index record if possible. */
126
103
static
127
104
ibool
128
105
row_purge_remove_clust_if_poss_low(
129
106
/*===============================*/
130
 
        purge_node_t*   node,   /*!< in: row purge node */
131
 
        ulint           mode)   /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
 
107
                                /* out: TRUE if success, or if not found, or
 
108
                                if modified after the delete marking */
 
109
        purge_node_t*   node,   /* in: row purge node */
 
110
        ulint           mode)   /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
132
111
{
133
112
        dict_index_t*   index;
134
113
        btr_pcur_t*     pcur;
146
125
        pcur = &(node->pcur);
147
126
        btr_cur = btr_pcur_get_btr_cur(pcur);
148
127
 
149
 
        log_free_check();
150
128
        mtr_start(&mtr);
151
129
 
152
130
        success = row_purge_reposition_pcur(mode, node, &mtr);
161
139
 
162
140
        rec = btr_pcur_get_rec(pcur);
163
141
 
164
 
        if (node->roll_ptr != row_get_rec_roll_ptr(
165
 
                    rec, index, rec_get_offsets(rec, index, offsets_,
166
 
                                                ULINT_UNDEFINED, &heap))) {
 
142
        if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(
 
143
                                       rec, index, rec_get_offsets(
 
144
                                               rec, index, offsets_,
 
145
                                               ULINT_UNDEFINED, &heap)))) {
167
146
                if (UNIV_LIKELY_NULL(heap)) {
168
147
                        mem_heap_free(heap);
169
148
                }
198
177
        return(success);
199
178
}
200
179
 
201
 
/***********************************************************//**
 
180
/***************************************************************
202
181
Removes a clustered index record if it has not been modified after the delete
203
182
marking. */
204
183
static
205
184
void
206
185
row_purge_remove_clust_if_poss(
207
186
/*===========================*/
208
 
        purge_node_t*   node)   /*!< in: row purge node */
 
187
        purge_node_t*   node)   /* in: row purge node */
209
188
{
210
189
        ibool   success;
211
190
        ulint   n_tries = 0;
234
213
        ut_a(success);
235
214
}
236
215
 
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.
242
 
 
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
250
 
page latch.
251
 
@return TRUE if the secondary index record can be purged */
252
 
UNIV_INTERN
253
 
ibool
254
 
row_purge_poss_sec(
255
 
/*===============*/
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 */
259
 
{
260
 
        ibool   can_delete;
261
 
        mtr_t   mtr;
262
 
 
263
 
        ut_ad(!dict_index_is_clust(index));
264
 
        mtr_start(&mtr);
265
 
 
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),
269
 
                                                 &mtr, index, entry);
270
 
 
271
 
        btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
272
 
 
273
 
        return(can_delete);
274
 
}
275
 
 
276
216
/***************************************************************
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 */
 
217
Removes a secondary index entry if possible. */
280
218
static
281
219
ibool
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 */
 
220
row_purge_remove_sec_if_poss_low(
 
221
/*=============================*/
 
222
                                /* out: TRUE if success or if not found */
 
223
        purge_node_t*   node,   /* in: row purge node */
 
224
        dict_index_t*   index,  /* in: index */
 
225
        const dtuple_t* entry,  /* in: index entry */
 
226
        ulint           mode)   /* in: latch mode BTR_MODIFY_LEAF or
 
227
                                BTR_MODIFY_TREE */
287
228
{
288
 
        btr_pcur_t              pcur;
289
 
        btr_cur_t*              btr_cur;
290
 
        ibool                   success = TRUE;
291
 
        ulint                   err;
292
 
        mtr_t                   mtr;
293
 
        enum row_search_result  search_result;
 
229
        btr_pcur_t      pcur;
 
230
        btr_cur_t*      btr_cur;
 
231
        ibool           success;
 
232
        ibool           old_has = 0; /* remove warning */
 
233
        ibool           found;
 
234
        ulint           err;
 
235
        mtr_t           mtr;
 
236
        mtr_t           mtr_vers;
294
237
 
295
238
        log_free_check();
296
239
        mtr_start(&mtr);
297
240
 
298
 
        search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE,
299
 
                                               &pcur, &mtr);
 
241
        found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
300
242
 
301
 
        switch (search_result) {
302
 
        case ROW_NOT_FOUND:
 
243
        if (!found) {
303
244
                /* Not found.  This is a legitimate condition.  In a
304
245
                rollback, InnoDB will remove secondary recs that would
305
246
                be purged anyway.  Then the actual purge will not find
312
253
 
313
254
                /* fputs("PURGE:........sec entry not found\n", stderr); */
314
255
                /* dtuple_print(stderr, entry); */
315
 
                goto func_exit;
316
 
        case ROW_FOUND:
317
 
                break;
318
 
        case ROW_BUFFERED:
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. */
323
 
                ut_error;
 
256
 
 
257
                btr_pcur_close(&pcur);
 
258
                mtr_commit(&mtr);
 
259
 
 
260
                return(TRUE);
324
261
        }
325
262
 
326
263
        btr_cur = btr_pcur_get_btr_cur(&pcur);
329
266
        which cannot be purged yet, requires its existence. If some requires,
330
267
        we should do nothing. */
331
268
 
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)));
338
 
 
339
 
                btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
340
 
                                           RB_NONE, &mtr);
341
 
                switch (UNIV_EXPECT(err, DB_SUCCESS)) {
342
 
                case DB_SUCCESS:
343
 
                        break;
344
 
                case DB_OUT_OF_FILE_SPACE:
345
 
                        success = FALSE;
346
 
                        break;
347
 
                default:
348
 
                        ut_error;
 
269
        mtr_start(&mtr_vers);
 
270
 
 
271
        success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr_vers);
 
272
 
 
273
        if (success) {
 
274
                old_has = row_vers_old_has_index_entry(
 
275
                        TRUE, btr_pcur_get_rec(&(node->pcur)),
 
276
                        &mtr_vers, index, entry);
 
277
        }
 
278
 
 
279
        btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
 
280
 
 
281
        if (!success || !old_has) {
 
282
                /* Remove the index record */
 
283
 
 
284
                if (mode == BTR_MODIFY_LEAF) {
 
285
                        success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
286
                } else {
 
287
                        ut_ad(mode == BTR_MODIFY_TREE);
 
288
                        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
 
289
                                                   RB_NONE, &mtr);
 
290
                        success = err == DB_SUCCESS;
 
291
                        ut_a(success || err == DB_OUT_OF_FILE_SPACE);
349
292
                }
350
293
        }
351
294
 
352
 
func_exit:
353
295
        btr_pcur_close(&pcur);
354
296
        mtr_commit(&mtr);
355
297
 
357
299
}
358
300
 
359
301
/***************************************************************
360
 
Removes a secondary index entry without modifying the index tree,
361
 
if possible.
362
 
@return TRUE if success or if not found */
363
 
static
364
 
ibool
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 */
370
 
{
371
 
        mtr_t                   mtr;
372
 
        btr_pcur_t              pcur;
373
 
        enum row_search_result  search_result;
374
 
 
375
 
        log_free_check();
376
 
 
377
 
        mtr_start(&mtr);
378
 
 
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 = que_node_get_parent(node);
384
 
 
385
 
        search_result = row_search_index_entry(
386
 
                index, entry, BTR_MODIFY_LEAF | BTR_DELETE, &pcur, &mtr);
387
 
 
388
 
        switch (search_result) {
389
 
                ibool   success;
390
 
        case ROW_FOUND:
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);
395
 
 
396
 
                        /* Only delete-marked records should be purged. */
397
 
                        ut_ad(REC_INFO_DELETED_FLAG
398
 
                              & rec_get_info_bits(
399
 
                                      btr_cur_get_rec(btr_cur),
400
 
                                      dict_table_is_comp(index->table)));
401
 
 
402
 
                        if (!btr_cur_optimistic_delete(btr_cur, &mtr)) {
403
 
 
404
 
                                /* The index entry could not be deleted. */
405
 
                                success = FALSE;
406
 
                                goto func_exit;
407
 
                        }
408
 
                }
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. */
413
 
        case ROW_BUFFERED:
414
 
                /* The deletion was buffered. */
415
 
        case ROW_NOT_FOUND:
416
 
                /* The index entry does not exist, nothing to do. */
417
 
                success = TRUE;
418
 
        func_exit:
419
 
                btr_pcur_close(&pcur);
420
 
                mtr_commit(&mtr);
421
 
                return(success);
422
 
        }
423
 
 
424
 
        ut_error;
425
 
        return(FALSE);
426
 
}
427
 
 
428
 
/***********************************************************//**
429
302
Removes a secondary index entry if possible. */
430
303
UNIV_INLINE
431
304
void
432
305
row_purge_remove_sec_if_poss(
433
306
/*=========================*/
434
 
        purge_node_t*   node,   /*!< in: row purge node */
435
 
        dict_index_t*   index,  /*!< in: index */
436
 
        dtuple_t*       entry)  /*!< in: index entry */
 
307
        purge_node_t*   node,   /* in: row purge node */
 
308
        dict_index_t*   index,  /* in: index */
 
309
        dtuple_t*       entry)  /* in: index entry */
437
310
{
438
311
        ibool   success;
439
312
        ulint   n_tries         = 0;
440
313
 
441
314
        /*      fputs("Purge: Removing secondary record\n", stderr); */
442
315
 
443
 
        if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) {
 
316
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
317
                                                   BTR_MODIFY_LEAF);
 
318
        if (success) {
444
319
 
445
320
                return;
446
321
        }
447
322
retry:
448
 
        success = row_purge_remove_sec_if_poss_tree(node, index, entry);
 
323
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
324
                                                   BTR_MODIFY_TREE);
449
325
        /* The delete operation may fail if we have little
450
326
        file space left: TODO: easiest to crash the database
451
327
        and restart with more file space */
462
338
        ut_a(success);
463
339
}
464
340
 
465
 
/***********************************************************//**
 
341
/***************************************************************
466
342
Purges a delete marking of a record. */
467
343
static
468
344
void
469
345
row_purge_del_mark(
470
346
/*===============*/
471
 
        purge_node_t*   node)   /*!< in: row purge node */
 
347
        purge_node_t*   node)   /* in: row purge node */
472
348
{
473
349
        mem_heap_t*     heap;
474
350
        dtuple_t*       entry;
494
370
        row_purge_remove_clust_if_poss(node);
495
371
}
496
372
 
497
 
/***********************************************************//**
 
373
/***************************************************************
498
374
Purges an update of an existing record. Also purges an update of a delete
499
375
marked record if that record contained an externally stored field. */
500
376
static
501
377
void
502
378
row_purge_upd_exist_or_extern(
503
379
/*==========================*/
504
 
        purge_node_t*   node)   /*!< in: row purge node */
 
380
        purge_node_t*   node)   /* in: row purge node */
505
381
{
506
382
        mem_heap_t*     heap;
507
383
        dtuple_t*       entry;
608
484
        }
609
485
}
610
486
 
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! */
 
487
/***************************************************************
 
488
Parses the row reference and other info in a modify undo log record. */
615
489
static
616
490
ibool
617
491
row_purge_parse_undo_rec(
618
492
/*=====================*/
619
 
        purge_node_t*   node,   /*!< in: row undo node */
 
493
                                /* out: TRUE if purge operation required:
 
494
                                NOTE that then the CALLER must unfreeze
 
495
                                data dictionary! */
 
496
        purge_node_t*   node,   /* in: row undo node */
620
497
        ibool*          updated_extern,
621
 
                                /*!< out: TRUE if an externally stored field
 
498
                                /* out: TRUE if an externally stored field
622
499
                                was updated */
623
 
        que_thr_t*      thr)    /*!< in: query thread */
 
500
        que_thr_t*      thr)    /* in: query thread */
624
501
{
625
502
        dict_index_t*   clust_index;
626
503
        byte*           ptr;
627
504
        trx_t*          trx;
628
 
        undo_no_t       undo_no;
629
 
        table_id_t      table_id;
630
 
        trx_id_t        trx_id;
631
 
        roll_ptr_t      roll_ptr;
 
505
        dulint          undo_no;
 
506
        dulint          table_id;
 
507
        dulint          trx_id;
 
508
        dulint          roll_ptr;
632
509
        ulint           info_bits;
633
510
        ulint           type;
634
511
        ulint           cmpl_info;
711
588
        return(TRUE);
712
589
}
713
590
 
714
 
/***********************************************************//**
 
591
/***************************************************************
715
592
Fetches an undo log record and does the purge for the recorded operation.
716
593
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 */
 
594
parent node, which is always a query thread node. */
719
595
static
720
596
ulint
721
597
row_purge(
722
598
/*======*/
723
 
        purge_node_t*   node,   /*!< in: row purge node */
724
 
        que_thr_t*      thr)    /*!< in: query thread */
 
599
                                /* out: DB_SUCCESS if operation successfully
 
600
                                completed, else error code */
 
601
        purge_node_t*   node,   /* in: row purge node */
 
602
        que_thr_t*      thr)    /* in: query thread */
725
603
{
726
 
        roll_ptr_t      roll_ptr;
727
 
        ibool           purge_needed;
728
 
        ibool           updated_extern;
729
 
        trx_t*          trx;
 
604
        dulint  roll_ptr;
 
605
        ibool   purge_needed;
 
606
        ibool   updated_extern;
 
607
        trx_t*  trx;
730
608
 
731
609
        ut_ad(node && thr);
732
610
 
785
663
        return(DB_SUCCESS);
786
664
}
787
665
 
788
 
/***********************************************************//**
 
666
/***************************************************************
789
667
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 */
 
668
function used in an SQL execution graph. */
792
669
UNIV_INTERN
793
670
que_thr_t*
794
671
row_purge_step(
795
672
/*===========*/
796
 
        que_thr_t*      thr)    /*!< in: query thread */
 
673
                                /* out: query thread to run next or NULL */
 
674
        que_thr_t*      thr)    /* in: query thread */
797
675
{
798
676
        purge_node_t*   node;
799
 
#ifdef UNIV_DEBUG
800
677
        ulint           err;
801
 
#endif /* UNIV_DEBUG */
802
678
 
803
679
        ut_ad(thr);
804
680
 
806
682
 
807
683
        ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
808
684
 
809
 
#ifdef UNIV_DEBUG
810
 
        err =
811
 
#endif /* UNIV_DEBUG */
812
 
        row_purge(node, thr);
 
685
        err = row_purge(node, thr);
813
686
 
814
 
#ifdef UNIV_DEBUG
815
 
        ut_a(err == DB_SUCCESS);
816
 
#endif /* UNIV_DEBUG */
 
687
        ut_ad(err == DB_SUCCESS);
817
688
 
818
689
        return(thr);
819
690
}