~mysql/mysql-server/mysql-6.0

« back to all changes in this revision

Viewing changes to innobase/row/row0purge.c

  • Committer: monty at mysql
  • Date: 2001-02-17 12:19:19 UTC
  • mto: (554.1.1)
  • mto: This revision was merged to the branch mainline in revision 556.
  • Revision ID: sp1r-monty@donna.mysql.com-20010217121919-07904
Added Innobase to source distribution

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Purge obsolete records
 
3
 
 
4
(c) 1997 Innobase Oy
 
5
 
 
6
Created 3/14/1997 Heikki Tuuri
 
7
*******************************************************/
 
8
 
 
9
#include "row0purge.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "row0purge.ic"
 
13
#endif
 
14
 
 
15
#include "fsp0fsp.h"
 
16
#include "mach0data.h"
 
17
#include "trx0rseg.h"
 
18
#include "trx0trx.h"
 
19
#include "trx0roll.h"
 
20
#include "trx0undo.h"
 
21
#include "trx0purge.h"
 
22
#include "trx0rec.h"
 
23
#include "que0que.h"
 
24
#include "row0row.h"
 
25
#include "row0upd.h"
 
26
#include "row0vers.h"
 
27
#include "log0log.h"
 
28
 
 
29
/************************************************************************
 
30
Creates a purge node to a query graph. */
 
31
 
 
32
purge_node_t*
 
33
row_purge_node_create(
 
34
/*==================*/
 
35
                                /* out, own: purge node */
 
36
        que_thr_t*      parent, /* in: parent node, i.e., a thr node */
 
37
        mem_heap_t*     heap)   /* in: memory heap where created */
 
38
{
 
39
        purge_node_t*   node;
 
40
 
 
41
        ut_ad(parent && heap);
 
42
 
 
43
        node = mem_heap_alloc(heap, sizeof(purge_node_t));
 
44
 
 
45
        node->common.type = QUE_NODE_PURGE;
 
46
        node->common.parent = parent;
 
47
 
 
48
        node->heap = mem_heap_create(256);
 
49
 
 
50
        return(node);
 
51
}
 
52
 
 
53
/***************************************************************
 
54
Repositions the pcur in the purge node on the clustered index record,
 
55
if found. */
 
56
static
 
57
ibool
 
58
row_purge_reposition_pcur(
 
59
/*======================*/
 
60
                                /* out: TRUE if the record was found */
 
61
        ulint           mode,   /* in: latching mode */
 
62
        purge_node_t*   node,   /* in: row purge node */
 
63
        mtr_t*          mtr)    /* in: mtr */
 
64
{
 
65
        ibool   found;
 
66
 
 
67
        if (node->found_clust) {
 
68
                found = btr_pcur_restore_position(mode, &(node->pcur), mtr);
 
69
 
 
70
                return(found);
 
71
        }
 
72
 
 
73
        found = row_search_on_row_ref(&(node->pcur), mode, node->table,
 
74
                                                        node->ref, mtr);
 
75
        node->found_clust = found;
 
76
 
 
77
        if (found) {
 
78
                btr_pcur_store_position(&(node->pcur), mtr);
 
79
        }
 
80
 
 
81
        return(found);
 
82
}
 
83
 
 
84
/***************************************************************
 
85
Removes a delete marked clustered index record if possible. */
 
86
static
 
87
ibool
 
88
row_purge_remove_clust_if_poss_low(
 
89
/*===============================*/
 
90
                                /* out: TRUE if success, or if not found, or
 
91
                                if modified after the delete marking */
 
92
        purge_node_t*   node,   /* in: row purge node */
 
93
        que_thr_t*      thr,    /* in: query thread */
 
94
        ulint           mode)   /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
 
95
{
 
96
        dict_index_t*   index;
 
97
        btr_pcur_t*     pcur;
 
98
        btr_cur_t*      btr_cur;
 
99
        ibool           success;
 
100
        ulint           err;
 
101
        mtr_t           mtr;
 
102
 
 
103
        UT_NOT_USED(thr);
 
104
 
 
105
        index = dict_table_get_first_index(node->table);
 
106
        
 
107
        pcur = &(node->pcur);
 
108
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
109
 
 
110
        mtr_start(&mtr);
 
111
 
 
112
        success = row_purge_reposition_pcur(mode, node, &mtr);
 
113
 
 
114
        if (!success) {
 
115
                /* The record is already removed */
 
116
 
 
117
                btr_pcur_commit_specify_mtr(pcur, &mtr);
 
118
 
 
119
                return(TRUE);
 
120
        }
 
121
 
 
122
        if (0 != ut_dulint_cmp(node->roll_ptr,
 
123
                row_get_rec_roll_ptr(btr_pcur_get_rec(pcur), index))) {
 
124
                
 
125
                /* Someone else has modified the record later: do not remove */
 
126
                btr_pcur_commit_specify_mtr(pcur, &mtr);
 
127
 
 
128
                return(TRUE);
 
129
        }
 
130
 
 
131
        if (mode == BTR_MODIFY_LEAF) {
 
132
                success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
133
        } else {
 
134
                ut_ad(mode == BTR_MODIFY_TREE);
 
135
                btr_cur_pessimistic_delete(&err, FALSE, btr_cur, &mtr);
 
136
 
 
137
                if (err == DB_SUCCESS) {
 
138
                        success = TRUE;
 
139
                } else if (err == DB_OUT_OF_FILE_SPACE) {
 
140
                        success = FALSE;
 
141
                } else {
 
142
                        ut_a(0);
 
143
                }
 
144
        }
 
145
 
 
146
        btr_pcur_commit_specify_mtr(pcur, &mtr);
 
147
 
 
148
        return(success);
 
149
}
 
150
                
 
151
/***************************************************************
 
152
Removes a clustered index record if it has not been modified after the delete
 
153
marking. */
 
154
static
 
155
void
 
156
row_purge_remove_clust_if_poss(
 
157
/*===========================*/
 
158
        purge_node_t*   node,   /* in: row purge node */
 
159
        que_thr_t*      thr)    /* in: query thread */
 
160
{
 
161
        ibool   success;
 
162
        ulint   n_tries = 0;
 
163
        
 
164
/*      printf("Purge: Removing clustered record\n"); */
 
165
 
 
166
        success = row_purge_remove_clust_if_poss_low(node, thr,
 
167
                                                        BTR_MODIFY_LEAF);
 
168
        if (success) {
 
169
 
 
170
                return;
 
171
        }
 
172
retry:
 
173
        success = row_purge_remove_clust_if_poss_low(node, thr,
 
174
                                                        BTR_MODIFY_TREE);
 
175
        /* The delete operation may fail if we have little
 
176
        file space left: TODO: easiest to crash the database
 
177
        and restart with more file space */
 
178
 
 
179
        if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
 
180
                n_tries++;
 
181
 
 
182
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
 
183
                        
 
184
                goto retry;
 
185
        }
 
186
 
 
187
        ut_a(success);
 
188
}
 
189
                                                
 
190
/***************************************************************
 
191
Removes a secondary index entry if possible. */
 
192
static
 
193
ibool
 
194
row_purge_remove_sec_if_poss_low(
 
195
/*=============================*/
 
196
                                /* out: TRUE if success or if not found */
 
197
        purge_node_t*   node,   /* in: row purge node */
 
198
        que_thr_t*      thr,    /* in: query thread */
 
199
        dict_index_t*   index,  /* in: index */
 
200
        dtuple_t*       entry,  /* in: index entry */
 
201
        ulint           mode)   /* in: latch mode BTR_MODIFY_LEAF or
 
202
                                BTR_MODIFY_TREE */      
 
203
{
 
204
        btr_pcur_t      pcur;
 
205
        btr_cur_t*      btr_cur;
 
206
        ibool           success;
 
207
        ibool           old_has;
 
208
        ibool           found;
 
209
        ulint           err;
 
210
        mtr_t           mtr;
 
211
        mtr_t           mtr_vers;
 
212
        
 
213
        UT_NOT_USED(thr);
 
214
 
 
215
        log_free_check();
 
216
        mtr_start(&mtr);
 
217
        
 
218
        found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
 
219
 
 
220
        if (!found) {
 
221
                /* Not found */
 
222
 
 
223
                /* FIXME: printf("PURGE:........sec entry not found\n"); */
 
224
                /* dtuple_print(entry); */
 
225
 
 
226
                btr_pcur_close(&pcur);
 
227
                mtr_commit(&mtr);
 
228
 
 
229
                return(TRUE);
 
230
        }
 
231
 
 
232
        btr_cur = btr_pcur_get_btr_cur(&pcur);
 
233
        
 
234
        /* We should remove the index record if no later version of the row,
 
235
        which cannot be purged yet, requires its existence. If some requires,
 
236
        we should do nothing. */
 
237
 
 
238
        mtr_start(&mtr_vers);
 
239
 
 
240
        success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr_vers);
 
241
 
 
242
        if (success) {          
 
243
                old_has = row_vers_old_has_index_entry(TRUE,
 
244
                                        btr_pcur_get_rec(&(node->pcur)),
 
245
                                        &mtr_vers, index, entry);
 
246
        }
 
247
 
 
248
        btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
 
249
        
 
250
        if (!success || !old_has) {
 
251
                /* Remove the index record */
 
252
 
 
253
                if (mode == BTR_MODIFY_LEAF) {          
 
254
                        success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
255
                } else {
 
256
                        ut_ad(mode == BTR_MODIFY_TREE);
 
257
                        btr_cur_pessimistic_delete(&err, FALSE, btr_cur, &mtr);
 
258
 
 
259
                        if (err == DB_SUCCESS) {
 
260
                                success = TRUE;
 
261
                        } else if (err == DB_OUT_OF_FILE_SPACE) {
 
262
                                success = FALSE;
 
263
                        } else {
 
264
                                ut_a(0);
 
265
                        }
 
266
                }
 
267
        }
 
268
 
 
269
        btr_pcur_close(&pcur);
 
270
        mtr_commit(&mtr);
 
271
 
 
272
        return(success);
 
273
}
 
274
 
 
275
/***************************************************************
 
276
Removes a secondary index entry if possible. */
 
277
UNIV_INLINE
 
278
void
 
279
row_purge_remove_sec_if_poss(
 
280
/*=========================*/
 
281
        purge_node_t*   node,   /* in: row purge node */
 
282
        que_thr_t*      thr,    /* in: query thread */
 
283
        dict_index_t*   index,  /* in: index */
 
284
        dtuple_t*       entry)  /* in: index entry */
 
285
{
 
286
        ibool   success;
 
287
        ulint   n_tries         = 0;
 
288
        
 
289
/*      printf("Purge: Removing secondary record\n"); */
 
290
 
 
291
        success = row_purge_remove_sec_if_poss_low(node, thr, index, entry,
 
292
                                                        BTR_MODIFY_LEAF);
 
293
        if (success) {
 
294
 
 
295
                return;
 
296
        }
 
297
retry:
 
298
        success = row_purge_remove_sec_if_poss_low(node, thr, index, entry,
 
299
                                                        BTR_MODIFY_TREE);
 
300
        /* The delete operation may fail if we have little
 
301
        file space left: TODO: easiest to crash the database
 
302
        and restart with more file space */
 
303
 
 
304
        if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
 
305
                                
 
306
                n_tries++;
 
307
 
 
308
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
 
309
                                                        
 
310
                goto retry;
 
311
        }
 
312
 
 
313
        ut_a(success);
 
314
}
 
315
 
 
316
/***************************************************************
 
317
Purges a delete marking of a record. */
 
318
static
 
319
void
 
320
row_purge_del_mark(
 
321
/*===============*/
 
322
        purge_node_t*   node,   /* in: row purge node */
 
323
        que_thr_t*      thr)    /* in: query thread */
 
324
{
 
325
        mem_heap_t*     heap;
 
326
        dtuple_t*       entry;
 
327
        dict_index_t*   index;
 
328
        
 
329
        ut_ad(node && thr);
 
330
 
 
331
        heap = mem_heap_create(1024);
 
332
 
 
333
        while (node->index != NULL) {
 
334
                index = node->index;
 
335
 
 
336
                /* Build the index entry */
 
337
                entry = row_build_index_entry(node->row, index, heap);
 
338
 
 
339
                row_purge_remove_sec_if_poss(node, thr, index, entry);
 
340
 
 
341
                node->index = dict_table_get_next_index(node->index);
 
342
        }
 
343
 
 
344
        mem_heap_free(heap);    
 
345
 
 
346
        row_purge_remove_clust_if_poss(node, thr);
 
347
}
 
348
        
 
349
/***************************************************************
 
350
Purges an update of an existing record. */
 
351
static
 
352
void
 
353
row_purge_upd_exist(
 
354
/*================*/
 
355
        purge_node_t*   node,   /* in: row purge node */
 
356
        que_thr_t*      thr)    /* in: query thread */
 
357
{
 
358
        mem_heap_t*     heap;
 
359
        dtuple_t*       entry;
 
360
        dict_index_t*   index;
 
361
        
 
362
        ut_ad(node && thr);
 
363
 
 
364
        heap = mem_heap_create(1024);
 
365
 
 
366
        while (node->index != NULL) {
 
367
                index = node->index;
 
368
 
 
369
                if (row_upd_changes_ord_field(NULL, node->index,
 
370
                                                        node->update)) {
 
371
                        /* Build the older version of the index entry */
 
372
                        entry = row_build_index_entry(node->row, index, heap);
 
373
 
 
374
                        row_purge_remove_sec_if_poss(node, thr, index, entry);
 
375
                }
 
376
 
 
377
                node->index = dict_table_get_next_index(node->index);
 
378
        }
 
379
 
 
380
        mem_heap_free(heap);    
 
381
}
 
382
 
 
383
/***************************************************************
 
384
Parses the row reference and other info in a modify undo log record. */
 
385
static
 
386
ibool
 
387
row_purge_parse_undo_rec(
 
388
/*=====================*/
 
389
                                /* out: TRUE if purge operation required */
 
390
        purge_node_t*   node,   /* in: row undo node */
 
391
        que_thr_t*      thr)    /* in: query thread */
 
392
{
 
393
        dict_index_t*   clust_index;
 
394
        byte*           ptr;
 
395
        dulint          undo_no;
 
396
        dulint          table_id;
 
397
        dulint          trx_id;
 
398
        dulint          roll_ptr;
 
399
        ulint           info_bits;
 
400
        ulint           type;
 
401
        ulint           cmpl_info;
 
402
        
 
403
        ut_ad(node && thr);
 
404
        
 
405
        ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
 
406
                                                        &undo_no, &table_id);
 
407
        node->rec_type = type;
 
408
 
 
409
        if (type == TRX_UNDO_UPD_DEL_REC) {
 
410
 
 
411
                return(FALSE);
 
412
        }                       
 
413
 
 
414
        ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
 
415
                                                                &info_bits);
 
416
        node->table = NULL;
 
417
 
 
418
        if (type == TRX_UNDO_UPD_EXIST_REC
 
419
                                && cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
 
420
 
 
421
                /* Purge requires no changes to indexes: we may return */
 
422
 
 
423
                return(FALSE);
 
424
        }
 
425
        
 
426
        /* NOTE that the table has to be explicitly released later */
 
427
 
 
428
        /* TODO: currently nothing prevents dropping of table when purge
 
429
        is accessing it! */
 
430
 
 
431
        mutex_enter(&(dict_sys->mutex));
 
432
 
 
433
        node->table = dict_table_get_on_id_low(table_id, thr_get_trx(thr));
 
434
 
 
435
        rw_lock_x_lock(&(purge_sys->purge_is_running));
 
436
 
 
437
        mutex_exit(&(dict_sys->mutex));
 
438
        
 
439
        if (node->table == NULL) {
 
440
                /* The table has been dropped: no need to do purge */
 
441
 
 
442
                rw_lock_x_unlock(&(purge_sys->purge_is_running));
 
443
 
 
444
                return(FALSE);
 
445
        }
 
446
 
 
447
        clust_index = dict_table_get_first_index(node->table);
 
448
 
 
449
        ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
 
450
                                                                node->heap);
 
451
 
 
452
        ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
 
453
                                        roll_ptr, info_bits, node->heap,
 
454
                                        &(node->update));
 
455
 
 
456
        /* Read to the partial row the fields that occur in indexes */
 
457
 
 
458
        ptr = trx_undo_rec_get_partial_row(ptr, clust_index, &(node->row),
 
459
                                                                node->heap);
 
460
        return(TRUE);
 
461
}
 
462
 
 
463
/***************************************************************
 
464
Fetches an undo log record and does the purge for the recorded operation.
 
465
If none left, or the current purge completed, returns the control to the
 
466
parent node, which is always a query thread node. */
 
467
static
 
468
ulint
 
469
row_purge(
 
470
/*======*/
 
471
                                /* out: DB_SUCCESS if operation successfully
 
472
                                completed, else error code */
 
473
        purge_node_t*   node,   /* in: row purge node */
 
474
        que_thr_t*      thr)    /* in: query thread */
 
475
{
 
476
        dulint  roll_ptr;
 
477
        ibool   purge_needed;
 
478
        
 
479
        ut_ad(node && thr);
 
480
 
 
481
        node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr,
 
482
                                                &(node->reservation),
 
483
                                                node->heap);
 
484
        if (!node->undo_rec) {
 
485
                /* Purge completed for this query thread */
 
486
 
 
487
                thr->run_node = que_node_get_parent(node);
 
488
 
 
489
                return(DB_SUCCESS);
 
490
        }
 
491
 
 
492
        node->roll_ptr = roll_ptr;
 
493
 
 
494
        if (node->undo_rec == &trx_purge_dummy_rec) {
 
495
                purge_needed = FALSE;
 
496
        } else {
 
497
                purge_needed = row_purge_parse_undo_rec(node, thr);
 
498
        }
 
499
 
 
500
        if (purge_needed) {
 
501
                node->found_clust = FALSE;
 
502
        
 
503
                node->index = dict_table_get_next_index(
 
504
                                dict_table_get_first_index(node->table));
 
505
 
 
506
                if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
 
507
                        row_purge_upd_exist(node, thr);
 
508
                } else {
 
509
                        ut_ad(node->rec_type == TRX_UNDO_DEL_MARK_REC);
 
510
                        row_purge_del_mark(node, thr);
 
511
                }
 
512
 
 
513
                if (node->found_clust) {
 
514
                        btr_pcur_close(&(node->pcur));
 
515
                }
 
516
 
 
517
                rw_lock_x_unlock(&(purge_sys->purge_is_running));               
 
518
        }
 
519
 
 
520
        /* Do some cleanup */
 
521
        trx_purge_rec_release(node->reservation);
 
522
        mem_heap_empty(node->heap);
 
523
        
 
524
        thr->run_node = node;
 
525
 
 
526
        return(DB_SUCCESS);
 
527
}
 
528
 
 
529
/***************************************************************
 
530
Does the purge operation for a single undo log record. This is a high-level
 
531
function used in an SQL execution graph. */
 
532
 
 
533
que_thr_t*
 
534
row_purge_step(
 
535
/*===========*/
 
536
                                /* out: query thread to run next or NULL */
 
537
        que_thr_t*      thr)    /* in: query thread */
 
538
{
 
539
        purge_node_t*   node;
 
540
        ulint           err;
 
541
 
 
542
        ut_ad(thr);
 
543
        
 
544
        node = thr->run_node;
 
545
 
 
546
        ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
 
547
 
 
548
        err = row_purge(node, thr);
 
549
 
 
550
        ut_ad(err == DB_SUCCESS);
 
551
 
 
552
        return(thr);
 
553