~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: Brian Aker
  • Date: 2009-02-21 00:18:15 UTC
  • Revision ID: brian@tangent.org-20090221001815-x20e8h71e984lvs1
Completion (?) of uint conversion.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*****************************************************************************
2
 
 
3
 
Copyright (C) 1997, 2010, Innobase Oy. All Rights Reserved.
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
14
 
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
 
St, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
*****************************************************************************/
18
 
 
19
 
/**************************************************//**
20
 
@file row/row0purge.c
 
1
/******************************************************
21
2
Purge obsolete records
22
3
 
 
4
(c) 1997 Innobase Oy
 
5
 
23
6
Created 3/14/1997 Heikki Tuuri
24
7
*******************************************************/
25
8
 
44
27
#include "row0mysql.h"
45
28
#include "log0log.h"
46
29
 
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 */
 
30
/************************************************************************
 
31
Creates a purge node to a query graph. */
70
32
UNIV_INTERN
71
33
purge_node_t*
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 */
76
39
{
77
40
        purge_node_t*   node;
78
41
 
79
42
        ut_ad(parent && heap);
80
43
 
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));
82
45
 
83
46
        node->common.type = QUE_NODE_PURGE;
84
47
        node->common.parent = parent;
88
51
        return(node);
89
52
}
90
53
 
91
 
/***********************************************************//**
 
54
/***************************************************************
92
55
Repositions the pcur in the purge node on the clustered index record,
93
 
if found.
94
 
@return TRUE if the record was found */
 
56
if found. */
95
57
static
96
58
ibool
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 */
102
65
{
103
66
        ibool   found;
104
67
 
119
82
        return(found);
120
83
}
121
84
 
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 */
 
85
/***************************************************************
 
86
Removes a delete marked clustered index record if possible. */
126
87
static
127
88
ibool
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 */
132
95
{
133
96
        dict_index_t*   index;
134
97
        btr_pcur_t*     pcur;
146
109
        pcur = &(node->pcur);
147
110
        btr_cur = btr_pcur_get_btr_cur(pcur);
148
111
 
149
 
        log_free_check();
150
112
        mtr_start(&mtr);
151
113
 
152
114
        success = row_purge_reposition_pcur(mode, node, &mtr);
161
123
 
162
124
        rec = btr_pcur_get_rec(pcur);
163
125
 
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);
169
132
                }
198
161
        return(success);
199
162
}
200
163
 
201
 
/***********************************************************//**
 
164
/***************************************************************
202
165
Removes a clustered index record if it has not been modified after the delete
203
166
marking. */
204
167
static
205
168
void
206
169
row_purge_remove_clust_if_poss(
207
170
/*===========================*/
208
 
        purge_node_t*   node)   /*!< in: row purge node */
 
171
        purge_node_t*   node)   /* in: row purge node */
209
172
{
210
173
        ibool   success;
211
174
        ulint   n_tries = 0;
234
197
        ut_a(success);
235
198
}
236
199
 
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
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. */
280
202
static
281
203
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 */
 
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
 
211
                                BTR_MODIFY_TREE */
287
212
{
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;
 
213
        btr_pcur_t      pcur;
 
214
        btr_cur_t*      btr_cur;
 
215
        ibool           success;
 
216
        ibool           old_has = 0; /* remove warning */
 
217
        ibool           found;
 
218
        ulint           err;
 
219
        mtr_t           mtr;
 
220
        mtr_t*          mtr_vers;
294
221
 
295
222
        log_free_check();
296
223
        mtr_start(&mtr);
297
224
 
298
 
        search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE,
299
 
                                               &pcur, &mtr);
 
225
        found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
300
226
 
301
 
        switch (search_result) {
302
 
        case ROW_NOT_FOUND:
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. */
 
227
        if (!found) {
 
228
                /* Not found */
312
229
 
313
230
                /* fputs("PURGE:........sec entry not found\n", stderr); */
314
231
                /* 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;
 
232
 
 
233
                btr_pcur_close(&pcur);
 
234
                mtr_commit(&mtr);
 
235
 
 
236
                return(TRUE);
324
237
        }
325
238
 
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. */
331
244
 
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;
 
245
        mtr_vers = mem_alloc(sizeof(mtr_t));
 
246
 
 
247
        mtr_start(mtr_vers);
 
248
 
 
249
        success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr_vers);
 
250
 
 
251
        if (success) {
 
252
                old_has = row_vers_old_has_index_entry(
 
253
                        TRUE, btr_pcur_get_rec(&(node->pcur)),
 
254
                        mtr_vers, index, entry);
 
255
        }
 
256
 
 
257
        btr_pcur_commit_specify_mtr(&(node->pcur), mtr_vers);
 
258
 
 
259
        mem_free(mtr_vers);
 
260
 
 
261
        if (!success || !old_has) {
 
262
                /* Remove the index record */
 
263
 
 
264
                if (mode == BTR_MODIFY_LEAF) {
 
265
                        success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
266
                } else {
 
267
                        ut_ad(mode == BTR_MODIFY_TREE);
 
268
                        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
 
269
                                                   RB_NONE, &mtr);
 
270
                        success = err == DB_SUCCESS;
 
271
                        ut_a(success || err == DB_OUT_OF_FILE_SPACE);
349
272
                }
350
273
        }
351
274
 
352
 
func_exit:
353
275
        btr_pcur_close(&pcur);
354
276
        mtr_commit(&mtr);
355
277
 
357
279
}
358
280
 
359
281
/***************************************************************
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 = static_cast<que_thr_t *>(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
282
Removes a secondary index entry if possible. */
430
283
UNIV_INLINE
431
284
void
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 */
437
290
{
438
291
        ibool   success;
439
292
        ulint   n_tries         = 0;
440
293
 
441
294
        /*      fputs("Purge: Removing secondary record\n", stderr); */
442
295
 
443
 
        if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) {
 
296
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
297
                                                   BTR_MODIFY_LEAF);
 
298
        if (success) {
444
299
 
445
300
                return;
446
301
        }
447
302
retry:
448
 
        success = row_purge_remove_sec_if_poss_tree(node, index, entry);
 
303
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
304
                                                   BTR_MODIFY_TREE);
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 */
462
318
        ut_a(success);
463
319
}
464
320
 
465
 
/***********************************************************//**
 
321
/***************************************************************
466
322
Purges a delete marking of a record. */
467
323
static
468
324
void
469
325
row_purge_del_mark(
470
326
/*===============*/
471
 
        purge_node_t*   node)   /*!< in: row purge node */
 
327
        purge_node_t*   node)   /* in: row purge node */
472
328
{
473
329
        mem_heap_t*     heap;
474
330
        dtuple_t*       entry;
494
350
        row_purge_remove_clust_if_poss(node);
495
351
}
496
352
 
497
 
/***********************************************************//**
 
353
/***************************************************************
498
354
Purges an update of an existing record. Also purges an update of a delete
499
355
marked record if that record contained an externally stored field. */
500
356
static
501
357
void
502
358
row_purge_upd_exist_or_extern(
503
359
/*==========================*/
504
 
        purge_node_t*   node)   /*!< in: row purge node */
 
360
        purge_node_t*   node)   /* in: row purge node */
505
361
{
506
362
        mem_heap_t*     heap;
507
363
        dtuple_t*       entry;
608
464
        }
609
465
}
610
466
 
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. */
615
469
static
616
470
ibool
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
 
475
                                data dictionary! */
 
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
622
479
                                was updated */
623
 
        que_thr_t*      thr)    /*!< in: query thread */
 
480
        que_thr_t*      thr)    /* in: query thread */
624
481
{
625
482
        dict_index_t*   clust_index;
626
483
        byte*           ptr;
627
484
        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;
 
485
        dulint          undo_no;
 
486
        dulint          table_id;
 
487
        dulint          trx_id;
 
488
        dulint          roll_ptr;
632
489
        ulint           info_bits;
633
490
        ulint           type;
634
491
        ulint           cmpl_info;
711
568
        return(TRUE);
712
569
}
713
570
 
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. */
719
575
static
720
576
ulint
721
577
row_purge(
722
578
/*======*/
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 */
725
583
{
726
 
        roll_ptr_t      roll_ptr;
727
 
        ibool           purge_needed;
728
 
        ibool           updated_extern;
729
 
        trx_t*          trx;
 
584
        dulint  roll_ptr;
 
585
        ibool   purge_needed;
 
586
        ibool   updated_extern;
 
587
        trx_t*  trx;
730
588
 
731
589
        ut_ad(node && thr);
732
590
 
785
643
        return(DB_SUCCESS);
786
644
}
787
645
 
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. */
792
649
UNIV_INTERN
793
650
que_thr_t*
794
651
row_purge_step(
795
652
/*===========*/
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 */
797
655
{
798
656
        purge_node_t*   node;
799
 
#ifdef UNIV_DEBUG
800
657
        ulint           err;
801
 
#endif /* UNIV_DEBUG */
802
658
 
803
659
        ut_ad(thr);
804
660
 
805
 
        node = static_cast<purge_node_t *>(thr->run_node);
 
661
        node = thr->run_node;
806
662
 
807
663
        ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
808
664
 
809
 
#ifdef UNIV_DEBUG
810
 
        err =
811
 
#endif /* UNIV_DEBUG */
812
 
        row_purge(node, thr);
 
665
        err = row_purge(node, thr);
813
666
 
814
 
#ifdef UNIV_DEBUG
815
 
        ut_a(err == DB_SUCCESS);
816
 
#endif /* UNIV_DEBUG */
 
667
        ut_ad(err == DB_SUCCESS);
817
668
 
818
669
        return(thr);
819
670
}