~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/innobase/row/row0uins.c

  • Committer: Monty Taylor
  • Date: 2010-09-26 21:24:15 UTC
  • mto: (1796.1.2 build)
  • mto: This revision was merged to the branch mainline in revision 1797.
  • Revision ID: mordred@inaugust.com-20100926212415-5fn3p3q75pgiei7r
Moved protocol doc into the docs.

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/row0uins.c
21
 
Fresh insert undo
22
 
 
23
 
Created 2/25/1997 Heikki Tuuri
24
 
*******************************************************/
25
 
 
26
 
#include "row0uins.h"
27
 
 
28
 
#ifdef UNIV_NONINL
29
 
#include "row0uins.ic"
30
 
#endif
31
 
 
32
 
#include "dict0dict.h"
33
 
#include "dict0boot.h"
34
 
#include "dict0crea.h"
35
 
#include "trx0undo.h"
36
 
#include "trx0roll.h"
37
 
#include "btr0btr.h"
38
 
#include "mach0data.h"
39
 
#include "row0undo.h"
40
 
#include "row0vers.h"
41
 
#include "trx0trx.h"
42
 
#include "trx0rec.h"
43
 
#include "row0row.h"
44
 
#include "row0upd.h"
45
 
#include "que0que.h"
46
 
#include "ibuf0ibuf.h"
47
 
#include "log0log.h"
48
 
 
49
 
/*************************************************************************
50
 
IMPORTANT NOTE: Any operation that generates redo MUST check that there
51
 
is enough space in the redo log before for that operation. This is
52
 
done by calling log_free_check(). The reason for checking the
53
 
availability of the redo log space before the start of the operation is
54
 
that we MUST not hold any synchonization objects when performing the
55
 
check.
56
 
If you make a change in this module make sure that no codepath is
57
 
introduced where a call to log_free_check() is bypassed. */
58
 
 
59
 
/*************************************************************************
60
 
IMPORTANT NOTE: Any operation that generates redo MUST check that there
61
 
is enough space in the redo log before for that operation. This is
62
 
done by calling log_free_check(). The reason for checking the
63
 
availability of the redo log space before the start of the operation is
64
 
that we MUST not hold any synchonization objects when performing the
65
 
check.
66
 
If you make a change in this module make sure that no codepath is
67
 
introduced where a call to log_free_check() is bypassed. */
68
 
 
69
 
/***************************************************************//**
70
 
Removes a clustered index record. The pcur in node was positioned on the
71
 
record, now it is detached.
72
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
73
 
static
74
 
ulint
75
 
row_undo_ins_remove_clust_rec(
76
 
/*==========================*/
77
 
        undo_node_t*    node)   /*!< in: undo node */
78
 
{
79
 
        btr_cur_t*      btr_cur;
80
 
        ibool           success;
81
 
        ulint           err;
82
 
        ulint           n_tries         = 0;
83
 
        mtr_t           mtr;
84
 
 
85
 
        mtr_start(&mtr);
86
 
 
87
 
        success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur),
88
 
                                            &mtr);
89
 
        ut_a(success);
90
 
 
91
 
        if (node->table->id == DICT_INDEXES_ID) {
92
 
                ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
93
 
 
94
 
                /* Drop the index tree associated with the row in
95
 
                SYS_INDEXES table: */
96
 
 
97
 
                dict_drop_index_tree(btr_pcur_get_rec(&(node->pcur)), &mtr);
98
 
 
99
 
                mtr_commit(&mtr);
100
 
 
101
 
                mtr_start(&mtr);
102
 
 
103
 
                success = btr_pcur_restore_position(BTR_MODIFY_LEAF,
104
 
                                                    &(node->pcur), &mtr);
105
 
                ut_a(success);
106
 
        }
107
 
 
108
 
        btr_cur = btr_pcur_get_btr_cur(&(node->pcur));
109
 
 
110
 
        success = btr_cur_optimistic_delete(btr_cur, &mtr);
111
 
 
112
 
        btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
113
 
 
114
 
        if (success) {
115
 
                trx_undo_rec_release(node->trx, node->undo_no);
116
 
 
117
 
                return(DB_SUCCESS);
118
 
        }
119
 
retry:
120
 
        /* If did not succeed, try pessimistic descent to tree */
121
 
        mtr_start(&mtr);
122
 
 
123
 
        success = btr_pcur_restore_position(BTR_MODIFY_TREE,
124
 
                                            &(node->pcur), &mtr);
125
 
        ut_a(success);
126
 
 
127
 
        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
128
 
                                   trx_is_recv(node->trx)
129
 
                                   ? RB_RECOVERY
130
 
                                   : RB_NORMAL, &mtr);
131
 
 
132
 
        /* The delete operation may fail if we have little
133
 
        file space left: TODO: easiest to crash the database
134
 
        and restart with more file space */
135
 
 
136
 
        if (err == DB_OUT_OF_FILE_SPACE
137
 
            && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
138
 
 
139
 
                btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
140
 
 
141
 
                n_tries++;
142
 
 
143
 
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
144
 
 
145
 
                goto retry;
146
 
        }
147
 
 
148
 
        btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
149
 
 
150
 
        trx_undo_rec_release(node->trx, node->undo_no);
151
 
 
152
 
        return(err);
153
 
}
154
 
 
155
 
/***************************************************************//**
156
 
Removes a secondary index entry if found.
157
 
@return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
158
 
static
159
 
ulint
160
 
row_undo_ins_remove_sec_low(
161
 
/*========================*/
162
 
        ulint           mode,   /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,
163
 
                                depending on whether we wish optimistic or
164
 
                                pessimistic descent down the index tree */
165
 
        dict_index_t*   index,  /*!< in: index */
166
 
        dtuple_t*       entry)  /*!< in: index entry to remove */
167
 
{
168
 
        btr_pcur_t              pcur;
169
 
        btr_cur_t*              btr_cur;
170
 
        ulint                   err;
171
 
        mtr_t                   mtr;
172
 
        enum row_search_result  search_result;
173
 
 
174
 
        mtr_start(&mtr);
175
 
 
176
 
        btr_cur = btr_pcur_get_btr_cur(&pcur);
177
 
 
178
 
        ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
179
 
 
180
 
        search_result = row_search_index_entry(index, entry, mode,
181
 
                                               &pcur, &mtr);
182
 
 
183
 
        switch (search_result) {
184
 
        case ROW_NOT_FOUND:
185
 
                err = DB_SUCCESS;
186
 
                goto func_exit;
187
 
        case ROW_FOUND:
188
 
                break;
189
 
        case ROW_BUFFERED:
190
 
        case ROW_NOT_DELETED_REF:
191
 
                /* These are invalid outcomes, because the mode passed
192
 
                to row_search_index_entry() did not include any of the
193
 
                flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
194
 
                ut_error;
195
 
        }
196
 
 
197
 
        if (mode == BTR_MODIFY_LEAF) {
198
 
                err = btr_cur_optimistic_delete(btr_cur, &mtr)
199
 
                        ? DB_SUCCESS : DB_FAIL;
200
 
        } else {
201
 
                ut_ad(mode == BTR_MODIFY_TREE);
202
 
 
203
 
                /* No need to distinguish RB_RECOVERY here, because we
204
 
                are deleting a secondary index record: the distinction
205
 
                between RB_NORMAL and RB_RECOVERY only matters when
206
 
                deleting a record that contains externally stored
207
 
                columns. */
208
 
                ut_ad(!dict_index_is_clust(index));
209
 
                btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
210
 
                                           RB_NORMAL, &mtr);
211
 
        }
212
 
func_exit:
213
 
        btr_pcur_close(&pcur);
214
 
        mtr_commit(&mtr);
215
 
 
216
 
        return(err);
217
 
}
218
 
 
219
 
/***************************************************************//**
220
 
Removes a secondary index entry from the index if found. Tries first
221
 
optimistic, then pessimistic descent down the tree.
222
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
223
 
static
224
 
ulint
225
 
row_undo_ins_remove_sec(
226
 
/*====================*/
227
 
        dict_index_t*   index,  /*!< in: index */
228
 
        dtuple_t*       entry)  /*!< in: index entry to insert */
229
 
{
230
 
        ulint   err;
231
 
        ulint   n_tries = 0;
232
 
 
233
 
        /* Try first optimistic descent to the B-tree */
234
 
 
235
 
        err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry);
236
 
 
237
 
        if (err == DB_SUCCESS) {
238
 
 
239
 
                return(err);
240
 
        }
241
 
 
242
 
        /* Try then pessimistic descent to the B-tree */
243
 
retry:
244
 
        err = row_undo_ins_remove_sec_low(BTR_MODIFY_TREE, index, entry);
245
 
 
246
 
        /* The delete operation may fail if we have little
247
 
        file space left: TODO: easiest to crash the database
248
 
        and restart with more file space */
249
 
 
250
 
        if (err != DB_SUCCESS && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
251
 
 
252
 
                n_tries++;
253
 
 
254
 
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
255
 
 
256
 
                goto retry;
257
 
        }
258
 
 
259
 
        return(err);
260
 
}
261
 
 
262
 
/***********************************************************//**
263
 
Parses the row reference and other info in a fresh insert undo record. */
264
 
static
265
 
void
266
 
row_undo_ins_parse_undo_rec(
267
 
/*========================*/
268
 
        undo_node_t*    node)   /*!< in/out: row undo node */
269
 
{
270
 
        dict_index_t*   clust_index;
271
 
        byte*           ptr;
272
 
        undo_no_t       undo_no;
273
 
        table_id_t      table_id;
274
 
        ulint           type;
275
 
        ulint           dummy;
276
 
        ibool           dummy_extern;
277
 
 
278
 
        ut_ad(node);
279
 
 
280
 
        ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy,
281
 
                                    &dummy_extern, &undo_no, &table_id);
282
 
        ut_ad(type == TRX_UNDO_INSERT_REC);
283
 
        node->rec_type = type;
284
 
 
285
 
        node->update = NULL;
286
 
        node->table = dict_table_get_on_id(table_id, node->trx);
287
 
 
288
 
        /* Skip the UNDO if we can't find the table or the .ibd file. */
289
 
        if (UNIV_UNLIKELY(node->table == NULL)) {
290
 
        } else if (UNIV_UNLIKELY(node->table->ibd_file_missing)) {
291
 
                node->table = NULL;
292
 
        } else {
293
 
                clust_index = dict_table_get_first_index(node->table);
294
 
 
295
 
                if (clust_index != NULL) {
296
 
                        ptr = trx_undo_rec_get_row_ref(
297
 
                                ptr, clust_index, &node->ref, node->heap);
298
 
                } else {
299
 
                        ut_print_timestamp(stderr);
300
 
                        fprintf(stderr, "  InnoDB: table ");
301
 
                        ut_print_name(stderr, node->trx, TRUE,
302
 
                                      node->table->name);
303
 
                        fprintf(stderr, " has no indexes, "
304
 
                                "ignoring the table\n");
305
 
 
306
 
                        node->table = NULL;
307
 
                }
308
 
        }
309
 
}
310
 
 
311
 
/***********************************************************//**
312
 
Undoes a fresh insert of a row to a table. A fresh insert means that
313
 
the same clustered index unique key did not have any record, even delete
314
 
marked, at the time of the insert.  InnoDB is eager in a rollback:
315
 
if it figures out that an index record will be removed in the purge
316
 
anyway, it will remove it in the rollback.
317
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
318
 
UNIV_INTERN
319
 
ulint
320
 
row_undo_ins(
321
 
/*=========*/
322
 
        undo_node_t*    node)   /*!< in: row undo node */
323
 
{
324
 
        ut_ad(node);
325
 
        ut_ad(node->state == UNDO_NODE_INSERT);
326
 
 
327
 
        row_undo_ins_parse_undo_rec(node);
328
 
 
329
 
        if (!node->table || !row_undo_search_clust_to_pcur(node)) {
330
 
                trx_undo_rec_release(node->trx, node->undo_no);
331
 
 
332
 
                return(DB_SUCCESS);
333
 
        }
334
 
 
335
 
        /* Iterate over all the indexes and undo the insert.*/
336
 
 
337
 
        /* Skip the clustered index (the first index) */
338
 
        node->index = dict_table_get_next_index(
339
 
                dict_table_get_first_index(node->table));
340
 
 
341
 
        while (node->index != NULL) {
342
 
                dtuple_t*       entry;
343
 
                ulint           err;
344
 
 
345
 
                entry = row_build_index_entry(node->row, node->ext,
346
 
                                              node->index, node->heap);
347
 
                if (UNIV_UNLIKELY(!entry)) {
348
 
                        /* The database must have crashed after
349
 
                        inserting a clustered index record but before
350
 
                        writing all the externally stored columns of
351
 
                        that record.  Because secondary index entries
352
 
                        are inserted after the clustered index record,
353
 
                        we may assume that the secondary index record
354
 
                        does not exist.  However, this situation may
355
 
                        only occur during the rollback of incomplete
356
 
                        transactions. */
357
 
                        ut_a(trx_is_recv(node->trx));
358
 
                } else {
359
 
                        log_free_check();
360
 
                        err = row_undo_ins_remove_sec(node->index, entry);
361
 
 
362
 
                        if (err != DB_SUCCESS) {
363
 
 
364
 
                                return(err);
365
 
                        }
366
 
                }
367
 
 
368
 
                node->index = dict_table_get_next_index(node->index);
369
 
        }
370
 
 
371
 
        log_free_check();
372
 
        return(row_undo_ins_remove_clust_rec(node));
373
 
}