~percona-dev/percona-innodb-plugin/percona-innodb-1.0

« back to all changes in this revision

Viewing changes to row/row0purge.c

  • Committer: Vadim Tkachenko
  • Date: 2008-12-01 02:05:57 UTC
  • Revision ID: vadim@percona.com-20081201020557-p7k2m94mjtdg1a83
New rw-locks

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 "row0mysql.h"
 
28
#include "log0log.h"
 
29
 
 
30
/************************************************************************
 
31
Creates a purge node to a query graph. */
 
32
UNIV_INTERN
 
33
purge_node_t*
 
34
row_purge_node_create(
 
35
/*==================*/
 
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 */
 
39
{
 
40
        purge_node_t*   node;
 
41
 
 
42
        ut_ad(parent && heap);
 
43
 
 
44
        node = mem_heap_alloc(heap, sizeof(purge_node_t));
 
45
 
 
46
        node->common.type = QUE_NODE_PURGE;
 
47
        node->common.parent = parent;
 
48
 
 
49
        node->heap = mem_heap_create(256);
 
50
 
 
51
        return(node);
 
52
}
 
53
 
 
54
/***************************************************************
 
55
Repositions the pcur in the purge node on the clustered index record,
 
56
if found. */
 
57
static
 
58
ibool
 
59
row_purge_reposition_pcur(
 
60
/*======================*/
 
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 */
 
65
{
 
66
        ibool   found;
 
67
 
 
68
        if (node->found_clust) {
 
69
                found = btr_pcur_restore_position(mode, &(node->pcur), mtr);
 
70
 
 
71
                return(found);
 
72
        }
 
73
 
 
74
        found = row_search_on_row_ref(&(node->pcur), mode, node->table,
 
75
                                      node->ref, mtr);
 
76
        node->found_clust = found;
 
77
 
 
78
        if (found) {
 
79
                btr_pcur_store_position(&(node->pcur), mtr);
 
80
        }
 
81
 
 
82
        return(found);
 
83
}
 
84
 
 
85
/***************************************************************
 
86
Removes a delete marked clustered index record if possible. */
 
87
static
 
88
ibool
 
89
row_purge_remove_clust_if_poss_low(
 
90
/*===============================*/
 
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 */
 
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
        rec_t*          rec;
 
103
        mem_heap_t*     heap            = NULL;
 
104
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
105
        rec_offs_init(offsets_);
 
106
 
 
107
        index = dict_table_get_first_index(node->table);
 
108
 
 
109
        pcur = &(node->pcur);
 
110
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
111
 
 
112
        mtr_start(&mtr);
 
113
 
 
114
        success = row_purge_reposition_pcur(mode, node, &mtr);
 
115
 
 
116
        if (!success) {
 
117
                /* The record is already removed */
 
118
 
 
119
                btr_pcur_commit_specify_mtr(pcur, &mtr);
 
120
 
 
121
                return(TRUE);
 
122
        }
 
123
 
 
124
        rec = btr_pcur_get_rec(pcur);
 
125
 
 
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)))) {
 
130
                if (UNIV_LIKELY_NULL(heap)) {
 
131
                        mem_heap_free(heap);
 
132
                }
 
133
                /* Someone else has modified the record later: do not remove */
 
134
                btr_pcur_commit_specify_mtr(pcur, &mtr);
 
135
 
 
136
                return(TRUE);
 
137
        }
 
138
 
 
139
        if (UNIV_LIKELY_NULL(heap)) {
 
140
                mem_heap_free(heap);
 
141
        }
 
142
 
 
143
        if (mode == BTR_MODIFY_LEAF) {
 
144
                success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
145
        } else {
 
146
                ut_ad(mode == BTR_MODIFY_TREE);
 
147
                btr_cur_pessimistic_delete(&err, FALSE, btr_cur, FALSE, &mtr);
 
148
 
 
149
                if (err == DB_SUCCESS) {
 
150
                        success = TRUE;
 
151
                } else if (err == DB_OUT_OF_FILE_SPACE) {
 
152
                        success = FALSE;
 
153
                } else {
 
154
                        ut_error;
 
155
                }
 
156
        }
 
157
 
 
158
        btr_pcur_commit_specify_mtr(pcur, &mtr);
 
159
 
 
160
        return(success);
 
161
}
 
162
 
 
163
/***************************************************************
 
164
Removes a clustered index record if it has not been modified after the delete
 
165
marking. */
 
166
static
 
167
void
 
168
row_purge_remove_clust_if_poss(
 
169
/*===========================*/
 
170
        purge_node_t*   node)   /* in: row purge node */
 
171
{
 
172
        ibool   success;
 
173
        ulint   n_tries = 0;
 
174
 
 
175
        /*      fputs("Purge: Removing clustered record\n", stderr); */
 
176
 
 
177
        success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF);
 
178
        if (success) {
 
179
 
 
180
                return;
 
181
        }
 
182
retry:
 
183
        success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_TREE);
 
184
        /* The delete operation may fail if we have little
 
185
        file space left: TODO: easiest to crash the database
 
186
        and restart with more file space */
 
187
 
 
188
        if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
 
189
                n_tries++;
 
190
 
 
191
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
 
192
 
 
193
                goto retry;
 
194
        }
 
195
 
 
196
        ut_a(success);
 
197
}
 
198
 
 
199
/***************************************************************
 
200
Removes a secondary index entry if possible. */
 
201
static
 
202
ibool
 
203
row_purge_remove_sec_if_poss_low(
 
204
/*=============================*/
 
205
                                /* out: TRUE if success or if not found */
 
206
        purge_node_t*   node,   /* in: row purge node */
 
207
        dict_index_t*   index,  /* in: index */
 
208
        dtuple_t*       entry,  /* in: index entry */
 
209
        ulint           mode)   /* in: latch mode BTR_MODIFY_LEAF or
 
210
                                BTR_MODIFY_TREE */
 
211
{
 
212
        btr_pcur_t      pcur;
 
213
        btr_cur_t*      btr_cur;
 
214
        ibool           success;
 
215
        ibool           old_has = 0; /* remove warning */
 
216
        ibool           found;
 
217
        ulint           err;
 
218
        mtr_t           mtr;
 
219
        mtr_t*          mtr_vers;
 
220
 
 
221
        log_free_check();
 
222
        mtr_start(&mtr);
 
223
 
 
224
        found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
 
225
 
 
226
        if (!found) {
 
227
                /* Not found */
 
228
 
 
229
                /* fputs("PURGE:........sec entry not found\n", stderr); */
 
230
                /* dtuple_print(stderr, entry); */
 
231
 
 
232
                btr_pcur_close(&pcur);
 
233
                mtr_commit(&mtr);
 
234
 
 
235
                return(TRUE);
 
236
        }
 
237
 
 
238
        btr_cur = btr_pcur_get_btr_cur(&pcur);
 
239
 
 
240
        /* We should remove the index record if no later version of the row,
 
241
        which cannot be purged yet, requires its existence. If some requires,
 
242
        we should do nothing. */
 
243
 
 
244
        mtr_vers = mem_alloc(sizeof(mtr_t));
 
245
 
 
246
        mtr_start(mtr_vers);
 
247
 
 
248
        success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr_vers);
 
249
 
 
250
        if (success) {
 
251
                old_has = row_vers_old_has_index_entry(
 
252
                        TRUE, btr_pcur_get_rec(&(node->pcur)),
 
253
                        mtr_vers, index, entry);
 
254
        }
 
255
 
 
256
        btr_pcur_commit_specify_mtr(&(node->pcur), mtr_vers);
 
257
 
 
258
        mem_free(mtr_vers);
 
259
 
 
260
        if (!success || !old_has) {
 
261
                /* Remove the index record */
 
262
 
 
263
                if (mode == BTR_MODIFY_LEAF) {
 
264
                        success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
265
                } else {
 
266
                        ut_ad(mode == BTR_MODIFY_TREE);
 
267
                        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
 
268
                                                   FALSE, &mtr);
 
269
                        success = err == DB_SUCCESS;
 
270
                        ut_a(success || err == DB_OUT_OF_FILE_SPACE);
 
271
                }
 
272
        }
 
273
 
 
274
        btr_pcur_close(&pcur);
 
275
        mtr_commit(&mtr);
 
276
 
 
277
        return(success);
 
278
}
 
279
 
 
280
/***************************************************************
 
281
Removes a secondary index entry if possible. */
 
282
UNIV_INLINE
 
283
void
 
284
row_purge_remove_sec_if_poss(
 
285
/*=========================*/
 
286
        purge_node_t*   node,   /* in: row purge node */
 
287
        dict_index_t*   index,  /* in: index */
 
288
        dtuple_t*       entry)  /* in: index entry */
 
289
{
 
290
        ibool   success;
 
291
        ulint   n_tries         = 0;
 
292
 
 
293
        /*      fputs("Purge: Removing secondary record\n", stderr); */
 
294
 
 
295
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
296
                                                   BTR_MODIFY_LEAF);
 
297
        if (success) {
 
298
 
 
299
                return;
 
300
        }
 
301
retry:
 
302
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
303
                                                   BTR_MODIFY_TREE);
 
304
        /* The delete operation may fail if we have little
 
305
        file space left: TODO: easiest to crash the database
 
306
        and restart with more file space */
 
307
 
 
308
        if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
 
309
 
 
310
                n_tries++;
 
311
 
 
312
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
 
313
 
 
314
                goto retry;
 
315
        }
 
316
 
 
317
        ut_a(success);
 
318
}
 
319
 
 
320
/***************************************************************
 
321
Purges a delete marking of a record. */
 
322
static
 
323
void
 
324
row_purge_del_mark(
 
325
/*===============*/
 
326
        purge_node_t*   node)   /* in: row purge node */
 
327
{
 
328
        mem_heap_t*     heap;
 
329
        dtuple_t*       entry;
 
330
        dict_index_t*   index;
 
331
 
 
332
        ut_ad(node);
 
333
 
 
334
        heap = mem_heap_create(1024);
 
335
 
 
336
        while (node->index != NULL) {
 
337
                index = node->index;
 
338
 
 
339
                /* Build the index entry */
 
340
                entry = row_build_index_entry(node->row, NULL, index, heap);
 
341
                ut_a(entry);
 
342
                row_purge_remove_sec_if_poss(node, index, entry);
 
343
 
 
344
                node->index = dict_table_get_next_index(node->index);
 
345
        }
 
346
 
 
347
        mem_heap_free(heap);
 
348
 
 
349
        row_purge_remove_clust_if_poss(node);
 
350
}
 
351
 
 
352
/***************************************************************
 
353
Purges an update of an existing record. Also purges an update of a delete
 
354
marked record if that record contained an externally stored field. */
 
355
static
 
356
void
 
357
row_purge_upd_exist_or_extern(
 
358
/*==========================*/
 
359
        purge_node_t*   node)   /* in: row purge node */
 
360
{
 
361
        mem_heap_t*     heap;
 
362
        dtuple_t*       entry;
 
363
        dict_index_t*   index;
 
364
        ibool           is_insert;
 
365
        ulint           rseg_id;
 
366
        ulint           page_no;
 
367
        ulint           offset;
 
368
        ulint           i;
 
369
        mtr_t           mtr;
 
370
 
 
371
        ut_ad(node);
 
372
 
 
373
        if (node->rec_type == TRX_UNDO_UPD_DEL_REC) {
 
374
 
 
375
                goto skip_secondaries;
 
376
        }
 
377
 
 
378
        heap = mem_heap_create(1024);
 
379
 
 
380
        while (node->index != NULL) {
 
381
                index = node->index;
 
382
 
 
383
                if (row_upd_changes_ord_field_binary(NULL, node->index,
 
384
                                                     node->update)) {
 
385
                        /* Build the older version of the index entry */
 
386
                        entry = row_build_index_entry(node->row, NULL,
 
387
                                                      index, heap);
 
388
                        ut_a(entry);
 
389
                        row_purge_remove_sec_if_poss(node, index, entry);
 
390
                }
 
391
 
 
392
                node->index = dict_table_get_next_index(node->index);
 
393
        }
 
394
 
 
395
        mem_heap_free(heap);
 
396
 
 
397
skip_secondaries:
 
398
        /* Free possible externally stored fields */
 
399
        for (i = 0; i < upd_get_n_fields(node->update); i++) {
 
400
 
 
401
                const upd_field_t*      ufield
 
402
                        = upd_get_nth_field(node->update, i);
 
403
 
 
404
                if (dfield_is_ext(&ufield->new_val)) {
 
405
                        buf_block_t*    block;
 
406
                        ulint           internal_offset;
 
407
                        byte*           data_field;
 
408
 
 
409
                        /* We use the fact that new_val points to
 
410
                        node->undo_rec and get thus the offset of
 
411
                        dfield data inside the undo record. Then we
 
412
                        can calculate from node->roll_ptr the file
 
413
                        address of the new_val data */
 
414
 
 
415
                        internal_offset
 
416
                                = ((const byte*)
 
417
                                   dfield_get_data(&ufield->new_val))
 
418
                                - node->undo_rec;
 
419
 
 
420
                        ut_a(internal_offset < UNIV_PAGE_SIZE);
 
421
 
 
422
                        trx_undo_decode_roll_ptr(node->roll_ptr,
 
423
                                                 &is_insert, &rseg_id,
 
424
                                                 &page_no, &offset);
 
425
                        mtr_start(&mtr);
 
426
 
 
427
                        /* We have to acquire an X-latch to the clustered
 
428
                        index tree */
 
429
 
 
430
                        index = dict_table_get_first_index(node->table);
 
431
 
 
432
                        mtr_x_lock(dict_index_get_lock(index), &mtr);
 
433
 
 
434
                        /* NOTE: we must also acquire an X-latch to the
 
435
                        root page of the tree. We will need it when we
 
436
                        free pages from the tree. If the tree is of height 1,
 
437
                        the tree X-latch does NOT protect the root page,
 
438
                        because it is also a leaf page. Since we will have a
 
439
                        latch on an undo log page, we would break the
 
440
                        latching order if we would only later latch the
 
441
                        root page of such a tree! */
 
442
 
 
443
                        btr_root_get(index, &mtr);
 
444
 
 
445
                        /* We assume in purge of externally stored fields
 
446
                        that the space id of the undo log record is 0! */
 
447
 
 
448
                        block = buf_page_get(0, 0, page_no, RW_X_LATCH, &mtr);
 
449
#ifdef UNIV_SYNC_DEBUG
 
450
                        buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
 
451
#endif /* UNIV_SYNC_DEBUG */
 
452
                        data_field = buf_block_get_frame(block)
 
453
                                + offset + internal_offset;
 
454
 
 
455
                        ut_a(dfield_get_len(&ufield->new_val)
 
456
                             >= BTR_EXTERN_FIELD_REF_SIZE);
 
457
                        btr_free_externally_stored_field(
 
458
                                index,
 
459
                                data_field + dfield_get_len(&ufield->new_val)
 
460
                                - BTR_EXTERN_FIELD_REF_SIZE,
 
461
                                NULL, NULL, NULL, 0, FALSE, &mtr);
 
462
                        mtr_commit(&mtr);
 
463
                }
 
464
        }
 
465
}
 
466
 
 
467
/***************************************************************
 
468
Parses the row reference and other info in a modify undo log record. */
 
469
static
 
470
ibool
 
471
row_purge_parse_undo_rec(
 
472
/*=====================*/
 
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 */
 
477
        ibool*          updated_extern,
 
478
                                /* out: TRUE if an externally stored field
 
479
                                was updated */
 
480
        que_thr_t*      thr)    /* in: query thread */
 
481
{
 
482
        dict_index_t*   clust_index;
 
483
        byte*           ptr;
 
484
        trx_t*          trx;
 
485
        dulint          undo_no;
 
486
        dulint          table_id;
 
487
        dulint          trx_id;
 
488
        dulint          roll_ptr;
 
489
        ulint           info_bits;
 
490
        ulint           type;
 
491
        ulint           cmpl_info;
 
492
 
 
493
        ut_ad(node && thr);
 
494
 
 
495
        trx = thr_get_trx(thr);
 
496
 
 
497
        ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
 
498
                                    updated_extern, &undo_no, &table_id);
 
499
        node->rec_type = type;
 
500
 
 
501
        if (type == TRX_UNDO_UPD_DEL_REC && !(*updated_extern)) {
 
502
 
 
503
                return(FALSE);
 
504
        }
 
505
 
 
506
        ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
 
507
                                               &info_bits);
 
508
        node->table = NULL;
 
509
 
 
510
        if (type == TRX_UNDO_UPD_EXIST_REC
 
511
            && cmpl_info & UPD_NODE_NO_ORD_CHANGE && !(*updated_extern)) {
 
512
 
 
513
                /* Purge requires no changes to indexes: we may return */
 
514
 
 
515
                return(FALSE);
 
516
        }
 
517
 
 
518
        /* Prevent DROP TABLE etc. from running when we are doing the purge
 
519
        for this row */
 
520
 
 
521
        row_mysql_freeze_data_dictionary(trx);
 
522
 
 
523
        mutex_enter(&(dict_sys->mutex));
 
524
 
 
525
        node->table = dict_table_get_on_id_low(table_id);
 
526
 
 
527
        mutex_exit(&(dict_sys->mutex));
 
528
 
 
529
        if (node->table == NULL) {
 
530
                /* The table has been dropped: no need to do purge */
 
531
err_exit:
 
532
                row_mysql_unfreeze_data_dictionary(trx);
 
533
                return(FALSE);
 
534
        }
 
535
 
 
536
        if (node->table->ibd_file_missing) {
 
537
                /* We skip purge of missing .ibd files */
 
538
 
 
539
                node->table = NULL;
 
540
 
 
541
                goto err_exit;
 
542
        }
 
543
 
 
544
        clust_index = dict_table_get_first_index(node->table);
 
545
 
 
546
        if (clust_index == NULL) {
 
547
                /* The table was corrupt in the data dictionary */
 
548
 
 
549
                goto err_exit;
 
550
        }
 
551
 
 
552
        ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
 
553
                                       node->heap);
 
554
 
 
555
        ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
 
556
                                             roll_ptr, info_bits, trx,
 
557
                                             node->heap, &(node->update));
 
558
 
 
559
        /* Read to the partial row the fields that occur in indexes */
 
560
 
 
561
        if (!(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
 
562
                ptr = trx_undo_rec_get_partial_row(ptr, clust_index,
 
563
                                                   &node->row, node->heap);
 
564
        }
 
565
 
 
566
        return(TRUE);
 
567
}
 
568
 
 
569
/***************************************************************
 
570
Fetches an undo log record and does the purge for the recorded operation.
 
571
If none left, or the current purge completed, returns the control to the
 
572
parent node, which is always a query thread node. */
 
573
static
 
574
ulint
 
575
row_purge(
 
576
/*======*/
 
577
                                /* out: DB_SUCCESS if operation successfully
 
578
                                completed, else error code */
 
579
        purge_node_t*   node,   /* in: row purge node */
 
580
        que_thr_t*      thr)    /* in: query thread */
 
581
{
 
582
        dulint  roll_ptr;
 
583
        ibool   purge_needed;
 
584
        ibool   updated_extern;
 
585
        trx_t*  trx;
 
586
 
 
587
        ut_ad(node && thr);
 
588
 
 
589
        trx = thr_get_trx(thr);
 
590
 
 
591
        node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr,
 
592
                                                  &(node->reservation),
 
593
                                                  node->heap);
 
594
        if (!node->undo_rec) {
 
595
                /* Purge completed for this query thread */
 
596
 
 
597
                thr->run_node = que_node_get_parent(node);
 
598
 
 
599
                return(DB_SUCCESS);
 
600
        }
 
601
 
 
602
        node->roll_ptr = roll_ptr;
 
603
 
 
604
        if (node->undo_rec == &trx_purge_dummy_rec) {
 
605
                purge_needed = FALSE;
 
606
        } else {
 
607
                purge_needed = row_purge_parse_undo_rec(node, &updated_extern,
 
608
                                                        thr);
 
609
                /* If purge_needed == TRUE, we must also remember to unfreeze
 
610
                data dictionary! */
 
611
        }
 
612
 
 
613
        if (purge_needed) {
 
614
                node->found_clust = FALSE;
 
615
 
 
616
                node->index = dict_table_get_next_index(
 
617
                        dict_table_get_first_index(node->table));
 
618
 
 
619
                if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
 
620
                        row_purge_del_mark(node);
 
621
 
 
622
                } else if (updated_extern
 
623
                           || node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
 
624
 
 
625
                        row_purge_upd_exist_or_extern(node);
 
626
                }
 
627
 
 
628
                if (node->found_clust) {
 
629
                        btr_pcur_close(&(node->pcur));
 
630
                }
 
631
 
 
632
                row_mysql_unfreeze_data_dictionary(trx);
 
633
        }
 
634
 
 
635
        /* Do some cleanup */
 
636
        trx_purge_rec_release(node->reservation);
 
637
        mem_heap_empty(node->heap);
 
638
 
 
639
        thr->run_node = node;
 
640
 
 
641
        return(DB_SUCCESS);
 
642
}
 
643
 
 
644
/***************************************************************
 
645
Does the purge operation for a single undo log record. This is a high-level
 
646
function used in an SQL execution graph. */
 
647
UNIV_INTERN
 
648
que_thr_t*
 
649
row_purge_step(
 
650
/*===========*/
 
651
                                /* out: query thread to run next or NULL */
 
652
        que_thr_t*      thr)    /* in: query thread */
 
653
{
 
654
        purge_node_t*   node;
 
655
        ulint           err;
 
656
 
 
657
        ut_ad(thr);
 
658
 
 
659
        node = thr->run_node;
 
660
 
 
661
        ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
 
662
 
 
663
        err = row_purge(node, thr);
 
664
 
 
665
        ut_ad(err == DB_SUCCESS);
 
666
 
 
667
        return(thr);
 
668
}