1
/******************************************************
6
Created 3/14/1997 Heikki Tuuri
7
*******************************************************/
12
#include "row0purge.ic"
16
#include "mach0data.h"
21
#include "trx0purge.h"
29
/************************************************************************
30
Creates a purge node to a query graph. */
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 */
41
ut_ad(parent && heap);
43
node = mem_heap_alloc(heap, sizeof(purge_node_t));
45
node->common.type = QUE_NODE_PURGE;
46
node->common.parent = parent;
48
node->heap = mem_heap_create(256);
53
/***************************************************************
54
Repositions the pcur in the purge node on the clustered index record,
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 */
67
if (node->found_clust) {
68
found = btr_pcur_restore_position(mode, &(node->pcur), mtr);
73
found = row_search_on_row_ref(&(node->pcur), mode, node->table,
75
node->found_clust = found;
78
btr_pcur_store_position(&(node->pcur), mtr);
84
/***************************************************************
85
Removes a delete marked clustered index record if possible. */
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 */
105
index = dict_table_get_first_index(node->table);
107
pcur = &(node->pcur);
108
btr_cur = btr_pcur_get_btr_cur(pcur);
112
success = row_purge_reposition_pcur(mode, node, &mtr);
115
/* The record is already removed */
117
btr_pcur_commit_specify_mtr(pcur, &mtr);
122
if (0 != ut_dulint_cmp(node->roll_ptr,
123
row_get_rec_roll_ptr(btr_pcur_get_rec(pcur), index))) {
125
/* Someone else has modified the record later: do not remove */
126
btr_pcur_commit_specify_mtr(pcur, &mtr);
131
if (mode == BTR_MODIFY_LEAF) {
132
success = btr_cur_optimistic_delete(btr_cur, &mtr);
134
ut_ad(mode == BTR_MODIFY_TREE);
135
btr_cur_pessimistic_delete(&err, FALSE, btr_cur, &mtr);
137
if (err == DB_SUCCESS) {
139
} else if (err == DB_OUT_OF_FILE_SPACE) {
146
btr_pcur_commit_specify_mtr(pcur, &mtr);
151
/***************************************************************
152
Removes a clustered index record if it has not been modified after the delete
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 */
164
/* printf("Purge: Removing clustered record\n"); */
166
success = row_purge_remove_clust_if_poss_low(node, thr,
173
success = row_purge_remove_clust_if_poss_low(node, thr,
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 */
179
if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
182
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
190
/***************************************************************
191
Removes a secondary index entry if possible. */
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
218
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
223
/* FIXME: printf("PURGE:........sec entry not found\n"); */
224
/* dtuple_print(entry); */
226
btr_pcur_close(&pcur);
232
btr_cur = btr_pcur_get_btr_cur(&pcur);
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. */
238
mtr_start(&mtr_vers);
240
success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr_vers);
243
old_has = row_vers_old_has_index_entry(TRUE,
244
btr_pcur_get_rec(&(node->pcur)),
245
&mtr_vers, index, entry);
248
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
250
if (!success || !old_has) {
251
/* Remove the index record */
253
if (mode == BTR_MODIFY_LEAF) {
254
success = btr_cur_optimistic_delete(btr_cur, &mtr);
256
ut_ad(mode == BTR_MODIFY_TREE);
257
btr_cur_pessimistic_delete(&err, FALSE, btr_cur, &mtr);
259
if (err == DB_SUCCESS) {
261
} else if (err == DB_OUT_OF_FILE_SPACE) {
269
btr_pcur_close(&pcur);
275
/***************************************************************
276
Removes a secondary index entry if possible. */
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 */
289
/* printf("Purge: Removing secondary record\n"); */
291
success = row_purge_remove_sec_if_poss_low(node, thr, index, entry,
298
success = row_purge_remove_sec_if_poss_low(node, thr, index, entry,
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 */
304
if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
308
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
316
/***************************************************************
317
Purges a delete marking of a record. */
322
purge_node_t* node, /* in: row purge node */
323
que_thr_t* thr) /* in: query thread */
331
heap = mem_heap_create(1024);
333
while (node->index != NULL) {
336
/* Build the index entry */
337
entry = row_build_index_entry(node->row, index, heap);
339
row_purge_remove_sec_if_poss(node, thr, index, entry);
341
node->index = dict_table_get_next_index(node->index);
346
row_purge_remove_clust_if_poss(node, thr);
349
/***************************************************************
350
Purges an update of an existing record. */
355
purge_node_t* node, /* in: row purge node */
356
que_thr_t* thr) /* in: query thread */
364
heap = mem_heap_create(1024);
366
while (node->index != NULL) {
369
if (row_upd_changes_ord_field(NULL, node->index,
371
/* Build the older version of the index entry */
372
entry = row_build_index_entry(node->row, index, heap);
374
row_purge_remove_sec_if_poss(node, thr, index, entry);
377
node->index = dict_table_get_next_index(node->index);
383
/***************************************************************
384
Parses the row reference and other info in a modify undo log record. */
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 */
393
dict_index_t* clust_index;
405
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
406
&undo_no, &table_id);
407
node->rec_type = type;
409
if (type == TRX_UNDO_UPD_DEL_REC) {
414
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
418
if (type == TRX_UNDO_UPD_EXIST_REC
419
&& cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
421
/* Purge requires no changes to indexes: we may return */
426
/* NOTE that the table has to be explicitly released later */
428
/* TODO: currently nothing prevents dropping of table when purge
431
mutex_enter(&(dict_sys->mutex));
433
node->table = dict_table_get_on_id_low(table_id, thr_get_trx(thr));
435
rw_lock_x_lock(&(purge_sys->purge_is_running));
437
mutex_exit(&(dict_sys->mutex));
439
if (node->table == NULL) {
440
/* The table has been dropped: no need to do purge */
442
rw_lock_x_unlock(&(purge_sys->purge_is_running));
447
clust_index = dict_table_get_first_index(node->table);
449
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
452
ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
453
roll_ptr, info_bits, node->heap,
456
/* Read to the partial row the fields that occur in indexes */
458
ptr = trx_undo_rec_get_partial_row(ptr, clust_index, &(node->row),
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. */
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 */
481
node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr,
482
&(node->reservation),
484
if (!node->undo_rec) {
485
/* Purge completed for this query thread */
487
thr->run_node = que_node_get_parent(node);
492
node->roll_ptr = roll_ptr;
494
if (node->undo_rec == &trx_purge_dummy_rec) {
495
purge_needed = FALSE;
497
purge_needed = row_purge_parse_undo_rec(node, thr);
501
node->found_clust = FALSE;
503
node->index = dict_table_get_next_index(
504
dict_table_get_first_index(node->table));
506
if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
507
row_purge_upd_exist(node, thr);
509
ut_ad(node->rec_type == TRX_UNDO_DEL_MARK_REC);
510
row_purge_del_mark(node, thr);
513
if (node->found_clust) {
514
btr_pcur_close(&(node->pcur));
517
rw_lock_x_unlock(&(purge_sys->purge_is_running));
520
/* Do some cleanup */
521
trx_purge_rec_release(node->reservation);
522
mem_heap_empty(node->heap);
524
thr->run_node = node;
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. */
536
/* out: query thread to run next or NULL */
537
que_thr_t* thr) /* in: query thread */
544
node = thr->run_node;
546
ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
548
err = row_purge(node, thr);
550
ut_ad(err == DB_SUCCESS);