MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
row0uins.cc
Go to the documentation of this file.
1 /*****************************************************************************
2 
3 Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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.,
15 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************/
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 "row0log.h"
42 #include "trx0trx.h"
43 #include "trx0rec.h"
44 #include "row0row.h"
45 #include "row0upd.h"
46 #include "que0que.h"
47 #include "ibuf0ibuf.h"
48 #include "log0log.h"
49 
50 /*************************************************************************
51 IMPORTANT NOTE: Any operation that generates redo MUST check that there
52 is enough space in the redo log before for that operation. This is
53 done by calling log_free_check(). The reason for checking the
54 availability of the redo log space before the start of the operation is
55 that we MUST not hold any synchonization objects when performing the
56 check.
57 If you make a change in this module make sure that no codepath is
58 introduced where a call to log_free_check() is bypassed. */
59 
60 /***************************************************************/
64 static __attribute__((nonnull, warn_unused_result))
65 dberr_t
66 row_undo_ins_remove_clust_rec(
67 /*==========================*/
68  undo_node_t* node)
69 {
70  btr_cur_t* btr_cur;
71  ibool success;
72  dberr_t err;
73  ulint n_tries = 0;
74  mtr_t mtr;
75  dict_index_t* index = node->pcur.btr_cur.index;
76  bool online;
77 
78  ut_ad(dict_index_is_clust(index));
79 
80  mtr_start(&mtr);
81 
82  /* This is similar to row_undo_mod_clust(). The DDL thread may
83  already have copied this row from the log to the new table.
84  We must log the removal, so that the row will be correctly
85  purged. However, we can log the removal out of sync with the
86  B-tree modification. */
87 
88  online = dict_index_is_online_ddl(index);
89  if (online) {
90  ut_ad(node->trx->dict_operation_lock_mode
91  != RW_X_LATCH);
92  ut_ad(node->table->id != DICT_INDEXES_ID);
93  mtr_s_lock(dict_index_get_lock(index), &mtr);
94  }
95 
96  success = btr_pcur_restore_position(
97  online
99  : BTR_MODIFY_LEAF, &node->pcur, &mtr);
100  ut_a(success);
101 
102  btr_cur = btr_pcur_get_btr_cur(&node->pcur);
103 
104  ut_ad(rec_get_trx_id(btr_cur_get_rec(btr_cur), btr_cur->index)
105  == node->trx->id);
106 
107  if (online && dict_index_is_online_ddl(index)) {
108  const rec_t* rec = btr_cur_get_rec(btr_cur);
109  mem_heap_t* heap = NULL;
110  const ulint* offsets = rec_get_offsets(
111  rec, index, NULL, ULINT_UNDEFINED, &heap);
113  rec, index, offsets, true, node->trx->id);
114  mem_heap_free(heap);
115  }
116 
117  if (node->table->id == DICT_INDEXES_ID) {
118  ut_ad(!online);
119  ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
120 
121  /* Drop the index tree associated with the row in
122  SYS_INDEXES table: */
123 
124  dict_drop_index_tree(btr_pcur_get_rec(&(node->pcur)), &mtr);
125 
126  mtr_commit(&mtr);
127 
128  mtr_start(&mtr);
129 
130  success = btr_pcur_restore_position(
131  BTR_MODIFY_LEAF, &node->pcur, &mtr);
132  ut_a(success);
133  }
134 
135  if (btr_cur_optimistic_delete(btr_cur, 0, &mtr)) {
136  err = DB_SUCCESS;
137  goto func_exit;
138  }
139 
140  btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
141 retry:
142  /* If did not succeed, try pessimistic descent to tree */
143  mtr_start(&mtr);
144 
145  success = btr_pcur_restore_position(BTR_MODIFY_TREE,
146  &(node->pcur), &mtr);
147  ut_a(success);
148 
149  btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
150  trx_is_recv(node->trx)
151  ? RB_RECOVERY
152  : RB_NORMAL, &mtr);
153 
154  /* The delete operation may fail if we have little
155  file space left: TODO: easiest to crash the database
156  and restart with more file space */
157 
158  if (err == DB_OUT_OF_FILE_SPACE
159  && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
160 
161  btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
162 
163  n_tries++;
164 
166 
167  goto retry;
168  }
169 
170 func_exit:
171  btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
172  trx_undo_rec_release(node->trx, node->undo_no);
173 
174  return(err);
175 }
176 
177 /***************************************************************/
180 static __attribute__((nonnull, warn_unused_result))
181 dberr_t
182 row_undo_ins_remove_sec_low(
183 /*========================*/
184  ulint mode,
187  dict_index_t* index,
188  dtuple_t* entry)
189 {
191  btr_cur_t* btr_cur;
192  dberr_t err = DB_SUCCESS;
193  mtr_t mtr;
194  enum row_search_result search_result;
195 
196  log_free_check();
197 
198  mtr_start(&mtr);
199 
200  if (mode == BTR_MODIFY_LEAF) {
202  mtr_s_lock(dict_index_get_lock(index), &mtr);
203  } else {
204  ut_ad(mode == BTR_MODIFY_TREE);
205  mtr_x_lock(dict_index_get_lock(index), &mtr);
206  }
207 
208  if (row_log_online_op_try(index, entry, 0)) {
209  goto func_exit_no_pcur;
210  }
211 
212  search_result = row_search_index_entry(index, entry, mode,
213  &pcur, &mtr);
214 
215  switch (search_result) {
216  case ROW_NOT_FOUND:
217  goto func_exit;
218  case ROW_FOUND:
219  break;
220  case ROW_BUFFERED:
221  case ROW_NOT_DELETED_REF:
222  /* These are invalid outcomes, because the mode passed
223  to row_search_index_entry() did not include any of the
224  flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
225  ut_error;
226  }
227 
228  btr_cur = btr_pcur_get_btr_cur(&pcur);
229 
230  if (mode != BTR_MODIFY_TREE) {
231  err = btr_cur_optimistic_delete(btr_cur, 0, &mtr)
232  ? DB_SUCCESS : DB_FAIL;
233  } else {
234  /* No need to distinguish RB_RECOVERY here, because we
235  are deleting a secondary index record: the distinction
236  between RB_NORMAL and RB_RECOVERY only matters when
237  deleting a record that contains externally stored
238  columns. */
239  ut_ad(!dict_index_is_clust(index));
240  btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
241  RB_NORMAL, &mtr);
242  }
243 func_exit:
244  btr_pcur_close(&pcur);
245 func_exit_no_pcur:
246  mtr_commit(&mtr);
247 
248  return(err);
249 }
250 
251 /***************************************************************/
255 static __attribute__((nonnull, warn_unused_result))
256 dberr_t
257 row_undo_ins_remove_sec(
258 /*====================*/
259  dict_index_t* index,
260  dtuple_t* entry)
261 {
262  dberr_t err;
263  ulint n_tries = 0;
264 
265  /* Try first optimistic descent to the B-tree */
266 
267  err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry);
268 
269  if (err == DB_SUCCESS) {
270 
271  return(err);
272  }
273 
274  /* Try then pessimistic descent to the B-tree */
275 retry:
276  err = row_undo_ins_remove_sec_low(BTR_MODIFY_TREE, index, entry);
277 
278  /* The delete operation may fail if we have little
279  file space left: TODO: easiest to crash the database
280  and restart with more file space */
281 
282  if (err != DB_SUCCESS && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
283 
284  n_tries++;
285 
287 
288  goto retry;
289  }
290 
291  return(err);
292 }
293 
294 /***********************************************************/
296 static
297 void
298 row_undo_ins_parse_undo_rec(
299 /*========================*/
300  undo_node_t* node,
301  ibool dict_locked)
302 {
303  dict_index_t* clust_index;
304  byte* ptr;
305  undo_no_t undo_no;
306  table_id_t table_id;
307  ulint type;
308  ulint dummy;
309  bool dummy_extern;
310 
311  ut_ad(node);
312 
313  ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy,
314  &dummy_extern, &undo_no, &table_id);
315  ut_ad(type == TRX_UNDO_INSERT_REC);
316  node->rec_type = type;
317 
318  node->update = NULL;
320  table_id, dict_locked, DICT_TABLE_OP_NORMAL);
321 
322  /* Skip the UNDO if we can't find the table or the .ibd file. */
323  if (UNIV_UNLIKELY(node->table == NULL)) {
324  } else if (UNIV_UNLIKELY(node->table->ibd_file_missing)) {
325 close_table:
326  dict_table_close(node->table, dict_locked, FALSE);
327  node->table = NULL;
328  } else {
329  clust_index = dict_table_get_first_index(node->table);
330 
331  if (clust_index != NULL) {
333  ptr, clust_index, &node->ref, node->heap);
334 
335  if (!row_undo_search_clust_to_pcur(node)) {
336  goto close_table;
337  }
338 
339  } else {
340  ut_print_timestamp(stderr);
341  fprintf(stderr, " InnoDB: table ");
342  ut_print_name(stderr, node->trx, TRUE,
343  node->table->name);
344  fprintf(stderr, " has no indexes, "
345  "ignoring the table\n");
346  goto close_table;
347  }
348  }
349 }
350 
351 /***************************************************************/
354 static __attribute__((nonnull, warn_unused_result))
355 dberr_t
356 row_undo_ins_remove_sec_rec(
357 /*========================*/
358  undo_node_t* node)
359 {
360  dberr_t err = DB_SUCCESS;
361  dict_index_t* index = node->index;
362  mem_heap_t* heap;
363 
364  heap = mem_heap_create(1024);
365 
366  while (index != NULL) {
367  dtuple_t* entry;
368 
369  if (index->type & DICT_FTS) {
370  dict_table_next_uncorrupted_index(index);
371  continue;
372  }
373 
374  /* An insert undo record TRX_UNDO_INSERT_REC will
375  always contain all fields of the index. It does not
376  matter if any indexes were created afterwards; all
377  index entries can be reconstructed from the row. */
378  entry = row_build_index_entry(
379  node->row, node->ext, index, heap);
380  if (UNIV_UNLIKELY(!entry)) {
381  /* The database must have crashed after
382  inserting a clustered index record but before
383  writing all the externally stored columns of
384  that record, or a statement is being rolled
385  back because an error occurred while storing
386  off-page columns.
387 
388  Because secondary index entries are inserted
389  after the clustered index record, we may
390  assume that the secondary index record does
391  not exist. */
392  } else {
393  err = row_undo_ins_remove_sec(index, entry);
394 
395  if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
396  goto func_exit;
397  }
398  }
399 
400  mem_heap_empty(heap);
401  dict_table_next_uncorrupted_index(index);
402  }
403 
404 func_exit:
405  node->index = index;
406  mem_heap_free(heap);
407  return(err);
408 }
409 
410 /***********************************************************/
417 UNIV_INTERN
418 dberr_t
420 /*=========*/
421  undo_node_t* node)
422 {
423  dberr_t err;
424  ibool dict_locked;
425 
426  ut_ad(node->state == UNDO_NODE_INSERT);
427 
428  dict_locked = node->trx->dict_operation_lock_mode == RW_X_LATCH;
429 
430  row_undo_ins_parse_undo_rec(node, dict_locked);
431 
432  if (node->table == NULL) {
433  trx_undo_rec_release(node->trx, node->undo_no);
434 
435  return(DB_SUCCESS);
436  }
437 
438  /* Iterate over all the indexes and undo the insert.*/
439 
440  node->index = dict_table_get_first_index(node->table);
442  /* Skip the clustered index (the first index) */
443  node->index = dict_table_get_next_index(node->index);
444 
445  dict_table_skip_corrupt_index(node->index);
446 
447  err = row_undo_ins_remove_sec_rec(node);
448 
449  if (err == DB_SUCCESS) {
450 
451  log_free_check();
452 
453  if (node->table->id == DICT_INDEXES_ID) {
454 
455  if (!dict_locked) {
456  mutex_enter(&dict_sys->mutex);
457  }
458  }
459 
460  // FIXME: We need to update the dict_index_t::space and
461  // page number fields too.
462  err = row_undo_ins_remove_clust_rec(node);
463 
464  if (node->table->id == DICT_INDEXES_ID
465  && !dict_locked) {
466 
467  mutex_exit(&dict_sys->mutex);
468  }
469  }
470 
471  dict_table_close(node->table, dict_locked, FALSE);
472 
473  node->table = NULL;
474 
475  return(err);
476 }