Drizzled Public API Documentation

trx0roll.cc
1 /*****************************************************************************
2 
3 Copyright (C) 1996, 2009, 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 /**************************************************/
26 #include "trx0roll.h"
27 
28 #ifdef UNIV_NONINL
29 #include "trx0roll.ic"
30 #endif
31 
32 #include "fsp0fsp.h"
33 #include "mach0data.h"
34 #include "trx0rseg.h"
35 #include "trx0trx.h"
36 #include "trx0undo.h"
37 #include "trx0rec.h"
38 #include "que0que.h"
39 #include "usr0sess.h"
40 #include "srv0start.h"
41 #include "row0undo.h"
42 #include "row0mysql.h"
43 #include "lock0lock.h"
44 #include "pars0pars.h"
45 
48 #define TRX_ROLL_TRUNC_THRESHOLD 1
49 
51 static trx_t* trx_roll_crash_recv_trx = NULL;
52 
55 static undo_no_t trx_roll_max_undo_no;
56 
58 static ulint trx_roll_progress_printed_pct;
59 
60 /*******************************************************************/
63 UNIV_INTERN
64 int
66 /*===========================*/
67  trx_t* trx,
68  trx_savept_t* savept)
71 {
72  mem_heap_t* heap;
73  que_thr_t* thr;
74  roll_node_t* roll_node;
75 
76  /* Tell Innobase server that there might be work for
77  utility threads: */
78 
80 
82 
83  heap = mem_heap_create(512);
84 
85  roll_node = roll_node_create(heap);
86 
87  if (savept) {
88  roll_node->partial = TRUE;
89  roll_node->savept = *savept;
90  }
91 
92  trx->error_state = DB_SUCCESS;
93 
94  thr = pars_complete_graph_for_exec(roll_node, trx, heap);
95 
96  ut_a(thr == que_fork_start_command(static_cast<que_fork_t *>(que_node_get_parent(thr))));
97  que_run_threads(thr);
98 
99  mutex_enter(&kernel_mutex);
100 
101  while (trx->que_state != TRX_QUE_RUNNING) {
102 
103  mutex_exit(&kernel_mutex);
104 
105  os_thread_sleep(100000);
106 
107  mutex_enter(&kernel_mutex);
108  }
109 
110  mutex_exit(&kernel_mutex);
111 
112  mem_heap_free(heap);
113 
114  ut_a(trx->error_state == DB_SUCCESS);
115 
116  /* Tell Innobase server that there might be work for
117  utility threads: */
118 
120 
121  return((int) trx->error_state);
122 }
123 
124 /*******************************************************************/
127 UNIV_INTERN
128 int
130 /*===================*/
131  trx_t* trx)
132 {
133  int err;
134 
135  if (trx->conc_state == TRX_NOT_STARTED) {
136 
137  return(DB_SUCCESS);
138  }
139 
140  trx->op_info = "rollback";
141 
142  /* If we are doing the XA recovery of prepared transactions, then
143  the transaction object does not have an InnoDB session object, and we
144  set a dummy session that we use for all MySQL transactions. */
145 
146  err = trx_general_rollback_for_mysql(trx, NULL);
147 
148  trx->op_info = "";
149 
150  return(err);
151 }
152 
153 /*******************************************************************/
156 UNIV_INTERN
157 int
159 /*=================================*/
160  trx_t* trx)
161 {
162  int err;
163 
164  if (trx->conc_state == TRX_NOT_STARTED) {
165 
166  return(DB_SUCCESS);
167  }
168 
169  trx->op_info = "rollback of SQL statement";
170 
171  err = trx_general_rollback_for_mysql(trx, &trx->last_sql_stat_start);
172  /* The following call should not be needed, but we play safe: */
174 
175  trx->op_info = "";
176 
177  return(err);
178 }
179 
180 /*******************************************************************/
182 UNIV_INTERN
183 void
185 /*=====================*/
186  trx_t* trx,
187  trx_named_savept_t* savep)
188 {
189  ut_a(savep != NULL);
191 
192  UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep);
193  mem_free(savep->name);
194  mem_free(savep);
195 }
196 
197 /*******************************************************************/
200 UNIV_INTERN
201 void
203 /*=====================*/
204  trx_t* trx,
205  trx_named_savept_t* savep)
208 {
209  trx_named_savept_t* next_savep;
210 
211  if (savep == NULL) {
212  savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
213  } else {
214  savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
215  }
216 
217  while (savep != NULL) {
218  next_savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
219 
220  trx_roll_savepoint_free(trx, savep);
221 
222  savep = next_savep;
223  }
224 }
225 
226 /*******************************************************************/
235 UNIV_INTERN
236 ulint
238 /*================================*/
239  trx_t* trx,
240  const char* savepoint_name,
241  ib_int64_t* mysql_binlog_cache_pos)
247 {
248  trx_named_savept_t* savep;
249  ulint err;
250 
251  savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
252 
253  while (savep != NULL) {
254  if (0 == ut_strcmp(savep->name, savepoint_name)) {
255  /* Found */
256  break;
257  }
258  savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
259  }
260 
261  if (savep == NULL) {
262 
263  return(DB_NO_SAVEPOINT);
264  }
265 
266  if (trx->conc_state == TRX_NOT_STARTED) {
267  ut_print_timestamp(stderr);
268  fputs(" InnoDB: Error: transaction has a savepoint ", stderr);
269  ut_print_name(stderr, trx, FALSE, savep->name);
270  fputs(" though it is not started\n", stderr);
271  return(DB_ERROR);
272  }
273 
274  /* We can now free all savepoints strictly later than this one */
275 
276  trx_roll_savepoints_free(trx, savep);
277 
278  *mysql_binlog_cache_pos = savep->mysql_binlog_cache_pos;
279 
280  trx->op_info = "rollback to a savepoint";
281 
282  err = trx_general_rollback_for_mysql(trx, &savep->savept);
283 
284  /* Store the current undo_no of the transaction so that we know where
285  to roll back if we have to roll back the next SQL statement: */
286 
288 
289  trx->op_info = "";
290 
291  return(err);
292 }
293 
294 /*******************************************************************/
300 UNIV_INTERN
301 ulint
303 /*====================*/
304  trx_t* trx,
305  const char* savepoint_name,
306  ib_int64_t binlog_cache_pos)
310 {
311  trx_named_savept_t* savep;
312 
313  ut_a(trx);
314  ut_a(savepoint_name);
315 
317 
318  savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
319 
320  while (savep != NULL) {
321  if (0 == ut_strcmp(savep->name, savepoint_name)) {
322  /* Found */
323  break;
324  }
325  savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
326  }
327 
328  if (savep) {
329  /* There is a savepoint with the same name: free that */
330 
331  UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep);
332 
333  mem_free(savep->name);
334  mem_free(savep);
335  }
336 
337  /* Create a new savepoint and add it as the last in the list */
338 
339  savep = static_cast<trx_named_savept_t *>(mem_alloc(sizeof(trx_named_savept_t)));
340 
341  savep->name = mem_strdup(savepoint_name);
342 
343  savep->savept = trx_savept_take(trx);
344 
345  savep->mysql_binlog_cache_pos = binlog_cache_pos;
346 
347  UT_LIST_ADD_LAST(trx_savepoints, trx->trx_savepoints, savep);
348 
349  return(DB_SUCCESS);
350 }
351 
352 /*******************************************************************/
357 UNIV_INTERN
358 ulint
360 /*============================*/
361  trx_t* trx,
362  const char* savepoint_name)
363 {
364  trx_named_savept_t* savep;
365 
366  savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
367 
368  /* Search for the savepoint by name and free if found. */
369  while (savep != NULL) {
370  if (0 == ut_strcmp(savep->name, savepoint_name)) {
371  trx_roll_savepoint_free(trx, savep);
372  return(DB_SUCCESS);
373  }
374  savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
375  }
376 
377  return(DB_NO_SAVEPOINT);
378 }
379 
380 /*******************************************************************/
385 UNIV_INTERN
386 ibool
388 /*========*/
389  const trx_t* trx)
390 {
391  return(trx == trx_roll_crash_recv_trx);
392 }
393 
394 /*******************************************************************/
397 UNIV_INTERN
400 /*============*/
401  trx_t* trx)
402 {
403  trx_savept_t savept;
404 
405  savept.least_undo_no = trx->undo_no;
406 
407  return(savept);
408 }
409 
410 /*******************************************************************/
412 static
413 void
414 trx_rollback_active(
415 /*================*/
416  trx_t* trx)
417 {
418  mem_heap_t* heap;
419  que_fork_t* fork;
420  que_thr_t* thr;
421  roll_node_t* roll_node;
422  dict_table_t* table;
423  ib_int64_t rows_to_undo;
424  const char* unit = "";
425  ibool dictionary_locked = FALSE;
426 
427  heap = mem_heap_create(512);
428 
429  fork = que_fork_create(NULL, NULL, QUE_FORK_RECOVERY, heap);
430  fork->trx = trx;
431 
432  thr = que_thr_create(fork, heap);
433 
434  roll_node = roll_node_create(heap);
435 
436  thr->child = roll_node;
437  roll_node->common.parent = thr;
438 
439  mutex_enter(&kernel_mutex);
440 
441  trx->graph = fork;
442 
443  ut_a(thr == que_fork_start_command(fork));
444 
445  trx_roll_crash_recv_trx = trx;
446  trx_roll_max_undo_no = trx->undo_no;
447  trx_roll_progress_printed_pct = 0;
448  rows_to_undo = trx_roll_max_undo_no;
449 
450  if (rows_to_undo > 1000000000) {
451  rows_to_undo = rows_to_undo / 1000000;
452  unit = "M";
453  }
454 
455  ut_print_timestamp(stderr);
456  fprintf(stderr,
457  " InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s"
458  " rows to undo\n",
459  trx->id,
460  (ulong) rows_to_undo, unit);
461  mutex_exit(&kernel_mutex);
462 
463  trx->mysql_thread_id = os_thread_get_curr_id();
464 
465  trx->mysql_process_no = os_proc_get_number();
466 
468  row_mysql_lock_data_dictionary(trx);
469  dictionary_locked = TRUE;
470  }
471 
472  que_run_threads(thr);
473 
474  mutex_enter(&kernel_mutex);
475 
476  while (trx->que_state != TRX_QUE_RUNNING) {
477 
478  mutex_exit(&kernel_mutex);
479 
480  fprintf(stderr,
481  "InnoDB: Waiting for rollback of trx id "
482  TRX_ID_FMT " to end\n",
483  trx->id);
484  os_thread_sleep(100000);
485 
486  mutex_enter(&kernel_mutex);
487  }
488 
489  mutex_exit(&kernel_mutex);
490 
492  && trx->table_id != 0) {
493 
494  /* If the transaction was for a dictionary operation, we
495  drop the relevant table, if it still exists */
496 
497  fprintf(stderr,
498  "InnoDB: Dropping table with id %llu"
499  " in recovery if it exists\n",
500  (ullint) trx->table_id);
501 
502  table = dict_table_get_on_id_low(trx->table_id);
503 
504  if (table) {
505  ulint err;
506 
507  fputs("InnoDB: Table found: dropping table ", stderr);
508  ut_print_name(stderr, trx, TRUE, table->name);
509  fputs(" in recovery\n", stderr);
510 
511  err = row_drop_table_for_mysql(table->name, trx, TRUE);
513 
514  ut_a(err == (int) DB_SUCCESS);
515  }
516  }
517 
518  if (dictionary_locked) {
520  }
521 
522  fprintf(stderr, "\nInnoDB: Rolling back of trx id " TRX_ID_FMT
523  " completed\n",
524  trx->id);
525  mem_heap_free(heap);
526 
527  trx_roll_crash_recv_trx = NULL;
528 }
529 
530 /*******************************************************************/
535 UNIV_INTERN
536 void
538 /*============================*/
539  ibool all)
541 {
542  trx_t* trx;
543 
544  mutex_enter(&kernel_mutex);
545 
546  if (!UT_LIST_GET_FIRST(trx_sys->trx_list)) {
547  goto leave_function;
548  }
549 
550  if (all) {
551  fprintf(stderr,
552  "InnoDB: Starting in background the rollback"
553  " of uncommitted transactions\n");
554  }
555 
556  mutex_exit(&kernel_mutex);
557 
558 loop:
559  mutex_enter(&kernel_mutex);
560 
561  for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx;
562  trx = UT_LIST_GET_NEXT(trx_list, trx)) {
563  if (!trx->is_recovered) {
564  continue;
565  }
566 
567  switch (trx->conc_state) {
568  case TRX_NOT_STARTED:
569  case TRX_PREPARED:
570  continue;
571 
572  case TRX_COMMITTED_IN_MEMORY:
573  mutex_exit(&kernel_mutex);
574  fprintf(stderr,
575  "InnoDB: Cleaning up trx with id "
576  TRX_ID_FMT "\n",
577  trx->id);
579  goto loop;
580 
581  case TRX_ACTIVE:
582  if (all || trx_get_dict_operation(trx)
583  != TRX_DICT_OP_NONE) {
584  mutex_exit(&kernel_mutex);
585  trx_rollback_active(trx);
586  goto loop;
587  }
588  }
589  }
590 
591  if (all) {
592  ut_print_timestamp(stderr);
593  fprintf(stderr,
594  " InnoDB: Rollback of non-prepared"
595  " transactions completed\n");
596  }
597 
598 leave_function:
599  mutex_exit(&kernel_mutex);
600 }
601 
602 /*******************************************************************/
609 UNIV_INTERN
610 os_thread_ret_t
612 /*================================*/
613  void* /*arg*/)
616 {
617 #ifdef UNIV_PFS_THREAD
618  pfs_register_thread(trx_rollback_clean_thread_key);
619 #endif /* UNIV_PFS_THREAD */
620 
622 
623  /* We count the number of threads in os_thread_exit(). A created
624  thread should always use that to exit and not use return() to exit. */
625 
626  os_thread_exit(NULL);
627 
628  OS_THREAD_DUMMY_RETURN;
629 }
630 
631 /*******************************************************************/
634 UNIV_INTERN
637 /*=====================*/
638 {
639  trx_undo_arr_t* arr;
640  mem_heap_t* heap;
641  ulint i;
642 
643  heap = mem_heap_create(1024);
644 
645  arr = static_cast<trx_undo_arr_t *>(mem_heap_alloc(heap, sizeof(trx_undo_arr_t)));
646 
647  arr->infos = static_cast<trx_undo_inf_t *>(mem_heap_alloc(heap, sizeof(trx_undo_inf_t)
648  * UNIV_MAX_PARALLELISM));
649  arr->n_cells = UNIV_MAX_PARALLELISM;
650  arr->n_used = 0;
651 
652  arr->heap = heap;
653 
654  for (i = 0; i < UNIV_MAX_PARALLELISM; i++) {
655 
656  (trx_undo_arr_get_nth_info(arr, i))->in_use = FALSE;
657  }
658 
659  return(arr);
660 }
661 
662 /*******************************************************************/
664 UNIV_INTERN
665 void
667 /*==============*/
668  trx_undo_arr_t* arr)
669 {
670  ut_ad(arr->n_used == 0);
671 
672  mem_heap_free(arr->heap);
673 }
674 
675 /*******************************************************************/
678 static
679 ibool
680 trx_undo_arr_store_info(
681 /*====================*/
682  trx_t* trx,
683  undo_no_t undo_no)
684 {
685  trx_undo_inf_t* cell;
686  trx_undo_inf_t* stored_here;
687  trx_undo_arr_t* arr;
688  ulint n_used;
689  ulint n;
690  ulint i;
691 
692  n = 0;
693  arr = trx->undo_no_arr;
694  n_used = arr->n_used;
695  stored_here = NULL;
696 
697  for (i = 0;; i++) {
698  cell = trx_undo_arr_get_nth_info(arr, i);
699 
700  if (!cell->in_use) {
701  if (!stored_here) {
702  /* Not in use, we may store here */
703  cell->undo_no = undo_no;
704  cell->in_use = TRUE;
705 
706  arr->n_used++;
707 
708  stored_here = cell;
709  }
710  } else {
711  n++;
712 
713  if (cell->undo_no == undo_no) {
714 
715  if (stored_here) {
716  stored_here->in_use = FALSE;
717  ut_ad(arr->n_used > 0);
718  arr->n_used--;
719  }
720 
721  ut_ad(arr->n_used == n_used);
722 
723  return(FALSE);
724  }
725  }
726 
727  if (n == n_used && stored_here) {
728 
729  ut_ad(arr->n_used == 1 + n_used);
730 
731  return(TRUE);
732  }
733  }
734 }
735 
736 /*******************************************************************/
738 static
739 void
740 trx_undo_arr_remove_info(
741 /*=====================*/
742  trx_undo_arr_t* arr,
743  undo_no_t undo_no)
744 {
745  trx_undo_inf_t* cell;
746  ulint i;
747 
748  for (i = 0;; i++) {
749  cell = trx_undo_arr_get_nth_info(arr, i);
750 
751  if (cell->in_use
752  && cell->undo_no == undo_no) {
753 
754  cell->in_use = FALSE;
755 
756  ut_ad(arr->n_used > 0);
757 
758  arr->n_used--;
759 
760  return;
761  }
762  }
763 }
764 
765 /*******************************************************************/
768 static
769 undo_no_t
770 trx_undo_arr_get_biggest(
771 /*=====================*/
772  trx_undo_arr_t* arr)
773 {
774  trx_undo_inf_t* cell;
775  ulint n_used;
776  undo_no_t biggest;
777  ulint n;
778  ulint i;
779 
780  n = 0;
781  n_used = arr->n_used;
782  biggest = 0;
783 
784  for (i = 0;; i++) {
785  cell = trx_undo_arr_get_nth_info(arr, i);
786 
787  if (cell->in_use) {
788  n++;
789  if (cell->undo_no > biggest) {
790 
791  biggest = cell->undo_no;
792  }
793  }
794 
795  if (n == n_used) {
796  return(biggest);
797  }
798  }
799 }
800 
801 /***********************************************************************/
803 UNIV_INTERN
804 void
806 /*==================*/
807  trx_t* trx)
808 {
809  trx_undo_arr_t* arr;
810  undo_no_t limit;
811  undo_no_t biggest;
812 
813  ut_ad(mutex_own(&(trx->undo_mutex)));
814  ut_ad(mutex_own(&((trx->rseg)->mutex)));
815 
816  trx->pages_undone = 0;
817 
818  arr = trx->undo_no_arr;
819 
820  limit = trx->undo_no;
821 
822  if (arr->n_used > 0) {
823  biggest = trx_undo_arr_get_biggest(arr);
824 
825  if (biggest >= limit) {
826 
827  limit = biggest + 1;
828  }
829  }
830 
831  if (trx->insert_undo) {
832  trx_undo_truncate_end(trx, trx->insert_undo, limit);
833  }
834 
835  if (trx->update_undo) {
836  trx_undo_truncate_end(trx, trx->update_undo, limit);
837  }
838 }
839 
840 /***********************************************************************/
844 static
846 trx_roll_pop_top_rec(
847 /*=================*/
848  trx_t* trx,
849  trx_undo_t* undo,
850  mtr_t* mtr)
851 {
852  page_t* undo_page;
853  ulint offset;
854  trx_undo_rec_t* prev_rec;
855  page_t* prev_rec_page;
856 
857  ut_ad(mutex_own(&(trx->undo_mutex)));
858 
859  undo_page = trx_undo_page_get_s_latched(undo->space, undo->zip_size,
860  undo->top_page_no, mtr);
861  offset = undo->top_offset;
862 
863  /* fprintf(stderr, "Thread %lu undoing trx " TRX_ID_FMT
864  " undo record " TRX_ID_FMT "\n",
865  os_thread_get_curr_id(), trx->id, undo->top_undo_no); */
866 
867  prev_rec = trx_undo_get_prev_rec(undo_page + offset,
868  undo->hdr_page_no, undo->hdr_offset,
869  mtr);
870  if (prev_rec == NULL) {
871 
872  undo->empty = TRUE;
873  } else {
874  prev_rec_page = page_align(prev_rec);
875 
876  if (prev_rec_page != undo_page) {
877 
878  trx->pages_undone++;
879  }
880 
881  undo->top_page_no = page_get_page_no(prev_rec_page);
882  undo->top_offset = prev_rec - prev_rec_page;
883  undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec);
884  }
885 
886  return(undo_page + offset);
887 }
888 
889 /********************************************************************/
897 UNIV_INTERN
900 /*========================*/
901  trx_t* trx,
902  undo_no_t limit,
903  roll_ptr_t* roll_ptr,
904  mem_heap_t* heap)
905 {
906  trx_undo_t* undo;
907  trx_undo_t* ins_undo;
908  trx_undo_t* upd_undo;
909  trx_undo_rec_t* undo_rec;
910  trx_undo_rec_t* undo_rec_copy;
911  undo_no_t undo_no;
912  ibool is_insert;
913  trx_rseg_t* rseg;
914  ulint progress_pct;
915  mtr_t mtr;
916 
917  rseg = trx->rseg;
918 try_again:
919  mutex_enter(&(trx->undo_mutex));
920 
921  if (trx->pages_undone >= TRX_ROLL_TRUNC_THRESHOLD) {
922  mutex_enter(&(rseg->mutex));
923 
925 
926  mutex_exit(&(rseg->mutex));
927  }
928 
929  ins_undo = trx->insert_undo;
930  upd_undo = trx->update_undo;
931 
932  if (!ins_undo || ins_undo->empty) {
933  undo = upd_undo;
934  } else if (!upd_undo || upd_undo->empty) {
935  undo = ins_undo;
936  } else if (upd_undo->top_undo_no > ins_undo->top_undo_no) {
937  undo = upd_undo;
938  } else {
939  undo = ins_undo;
940  }
941 
942  if (!undo || undo->empty
943  || limit > undo->top_undo_no) {
944 
945  if ((trx->undo_no_arr)->n_used == 0) {
946  /* Rollback is ending */
947 
948  mutex_enter(&(rseg->mutex));
949 
951 
952  mutex_exit(&(rseg->mutex));
953  }
954 
955  mutex_exit(&(trx->undo_mutex));
956 
957  return(NULL);
958  }
959 
960  if (undo == ins_undo) {
961  is_insert = TRUE;
962  } else {
963  is_insert = FALSE;
964  }
965 
966  *roll_ptr = trx_undo_build_roll_ptr(is_insert, (undo->rseg)->id,
967  undo->top_page_no,
968  undo->top_offset);
969  mtr_start(&mtr);
970 
971  undo_rec = trx_roll_pop_top_rec(trx, undo, &mtr);
972 
973  undo_no = trx_undo_rec_get_undo_no(undo_rec);
974 
975  ut_ad(undo_no + 1 == trx->undo_no);
976 
977  /* We print rollback progress info if we are in a crash recovery
978  and the transaction has at least 1000 row operations to undo. */
979 
980  if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) {
981 
982  progress_pct = 100 - (ulint)
983  ((undo_no * 100) / trx_roll_max_undo_no);
984  if (progress_pct != trx_roll_progress_printed_pct) {
985  if (trx_roll_progress_printed_pct == 0) {
986  fprintf(stderr,
987  "\nInnoDB: Progress in percents:"
988  " %lu", (ulong) progress_pct);
989  } else {
990  fprintf(stderr,
991  " %lu", (ulong) progress_pct);
992  }
993  fflush(stderr);
994  trx_roll_progress_printed_pct = progress_pct;
995  }
996  }
997 
998  trx->undo_no = undo_no;
999 
1000  if (!trx_undo_arr_store_info(trx, undo_no)) {
1001  /* A query thread is already processing this undo log record */
1002 
1003  mutex_exit(&(trx->undo_mutex));
1004 
1005  mtr_commit(&mtr);
1006 
1007  goto try_again;
1008  }
1009 
1010  undo_rec_copy = trx_undo_rec_copy(undo_rec, heap);
1011 
1012  mutex_exit(&(trx->undo_mutex));
1013 
1014  mtr_commit(&mtr);
1015 
1016  return(undo_rec_copy);
1017 }
1018 
1019 /********************************************************************/
1024 UNIV_INTERN
1025 ibool
1027 /*=================*/
1028  trx_t* trx,
1029  undo_no_t undo_no)
1030 {
1031  ibool ret;
1032 
1033  mutex_enter(&(trx->undo_mutex));
1034 
1035  ret = trx_undo_arr_store_info(trx, undo_no);
1036 
1037  mutex_exit(&(trx->undo_mutex));
1038 
1039  return(ret);
1040 }
1041 
1042 /*******************************************************************/
1044 UNIV_INTERN
1045 void
1047 /*=================*/
1048  trx_t* trx,
1049  undo_no_t undo_no)
1050 {
1051  trx_undo_arr_t* arr;
1052 
1053  mutex_enter(&(trx->undo_mutex));
1054 
1055  arr = trx->undo_no_arr;
1056 
1057  trx_undo_arr_remove_info(arr, undo_no);
1058 
1059  mutex_exit(&(trx->undo_mutex));
1060 }
1061 
1062 /*********************************************************************/
1064 UNIV_INTERN
1065 void
1067 /*=========*/
1068  trx_t* trx,
1069  trx_sig_t* sig,
1070  que_thr_t** next_thr)
1076 {
1077  que_t* roll_graph;
1078  que_thr_t* thr;
1079  /* que_thr_t* thr2; */
1080 
1081  ut_ad(mutex_own(&kernel_mutex));
1082  ut_ad((trx->undo_no_arr == NULL) || ((trx->undo_no_arr)->n_used == 0));
1083 
1084  /* Initialize the rollback field in the transaction */
1085 
1086  switch (sig->type) {
1087  case TRX_SIG_TOTAL_ROLLBACK:
1088  trx->roll_limit = 0;
1089  break;
1090  case TRX_SIG_ROLLBACK_TO_SAVEPT:
1091  trx->roll_limit = (sig->savept).least_undo_no;
1092  break;
1093  case TRX_SIG_ERROR_OCCURRED:
1094  trx->roll_limit = trx->last_sql_stat_start.least_undo_no;
1095  break;
1096  default:
1097  ut_error;
1098  }
1099 
1100  ut_a(trx->roll_limit <= trx->undo_no);
1101 
1102  trx->pages_undone = 0;
1103 
1104  if (trx->undo_no_arr == NULL) {
1106  }
1107 
1108  /* Build a 'query' graph which will perform the undo operations */
1109 
1110  roll_graph = trx_roll_graph_build(trx);
1111 
1112  trx->graph = roll_graph;
1113  trx->que_state = TRX_QUE_ROLLING_BACK;
1114 
1115  thr = que_fork_start_command(roll_graph);
1116 
1117  ut_ad(thr);
1118 
1119  /* thr2 = que_fork_start_command(roll_graph);
1120 
1121  ut_ad(thr2); */
1122 
1123  if (next_thr && (*next_thr == NULL)) {
1124  *next_thr = thr;
1125  /* srv_que_task_enqueue_low(thr2); */
1126  } else {
1128  /* srv_que_task_enqueue_low(thr2); */
1129  }
1130 }
1131 
1132 /****************************************************************/
1138 UNIV_INTERN
1139 que_t*
1141 /*=================*/
1142  trx_t* trx)
1143 {
1144  mem_heap_t* heap;
1145  que_fork_t* fork;
1146  que_thr_t* thr;
1147  /* que_thr_t* thr2; */
1148 
1149  ut_ad(mutex_own(&kernel_mutex));
1150 
1151  heap = mem_heap_create(512);
1152  fork = que_fork_create(NULL, NULL, QUE_FORK_ROLLBACK, heap);
1153  fork->trx = trx;
1154 
1155  thr = que_thr_create(fork, heap);
1156  /* thr2 = que_thr_create(fork, heap); */
1157 
1158  thr->child = row_undo_node_create(trx, thr, heap);
1159  /* thr2->child = row_undo_node_create(trx, thr2, heap); */
1160 
1161  return(fork);
1162 }
1163 
1164 /*********************************************************************/
1167 static
1168 void
1169 trx_finish_error_processing(
1170 /*========================*/
1171  trx_t* trx)
1172 {
1173  trx_sig_t* sig;
1174  trx_sig_t* next_sig;
1175 
1176  ut_ad(mutex_own(&kernel_mutex));
1177 
1178  sig = UT_LIST_GET_FIRST(trx->signals);
1179 
1180  while (sig != NULL) {
1181  next_sig = UT_LIST_GET_NEXT(signals, sig);
1182 
1183  if (sig->type == TRX_SIG_ERROR_OCCURRED) {
1184 
1185  trx_sig_remove(trx, sig);
1186  }
1187 
1188  sig = next_sig;
1189  }
1190 
1191  trx->que_state = TRX_QUE_RUNNING;
1192 }
1193 
1194 /*********************************************************************/
1196 static
1197 void
1198 trx_finish_partial_rollback_off_kernel(
1199 /*===================================*/
1200  trx_t* trx,
1201  que_thr_t** next_thr)
1206 {
1207  trx_sig_t* sig;
1208 
1209  ut_ad(mutex_own(&kernel_mutex));
1210 
1211  sig = UT_LIST_GET_FIRST(trx->signals);
1212 
1213  /* Remove the signal from the signal queue and send reply message
1214  to it */
1215 
1216  trx_sig_reply(sig, next_thr);
1217  trx_sig_remove(trx, sig);
1218 
1219  trx->que_state = TRX_QUE_RUNNING;
1220 }
1221 
1222 /****************************************************************/
1224 UNIV_INTERN
1225 void
1227 /*===========================*/
1228  que_t* graph,
1229  trx_t* trx,
1230  que_thr_t** next_thr)
1236 {
1237  trx_sig_t* sig;
1238  trx_sig_t* next_sig;
1239 
1240  ut_ad(mutex_own(&kernel_mutex));
1241 
1242  ut_a(trx->undo_no_arr == NULL || trx->undo_no_arr->n_used == 0);
1243 
1244  /* Free the memory reserved by the undo graph */
1245  que_graph_free(graph);
1246 
1247  sig = UT_LIST_GET_FIRST(trx->signals);
1248 
1249  if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) {
1250 
1251  trx_finish_partial_rollback_off_kernel(trx, next_thr);
1252 
1253  return;
1254 
1255  } else if (sig->type == TRX_SIG_ERROR_OCCURRED) {
1256 
1257  trx_finish_error_processing(trx);
1258 
1259  return;
1260  }
1261 
1262 #ifdef UNIV_DEBUG
1263  if (lock_print_waits) {
1264  fprintf(stderr, "Trx " TRX_ID_FMT " rollback finished\n",
1265  trx->id);
1266  }
1267 #endif /* UNIV_DEBUG */
1268 
1269  trx_commit_off_kernel(trx);
1270 
1271  /* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and
1272  send reply messages to them */
1273 
1274  trx->que_state = TRX_QUE_RUNNING;
1275 
1276  while (sig != NULL) {
1277  next_sig = UT_LIST_GET_NEXT(signals, sig);
1278 
1279  if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
1280 
1281  trx_sig_reply(sig, next_thr);
1282 
1283  trx_sig_remove(trx, sig);
1284  }
1285 
1286  sig = next_sig;
1287  }
1288 }
1289 
1290 /*********************************************************************/
1293 UNIV_INTERN
1294 roll_node_t*
1296 /*=============*/
1297  mem_heap_t* heap)
1298 {
1299  roll_node_t* node;
1300 
1301 
1302  node = static_cast<roll_node_t *>(mem_heap_alloc(heap, sizeof(roll_node_t)));
1303  node->common.type = QUE_NODE_ROLLBACK;
1304  node->state = ROLL_NODE_SEND;
1305 
1306  node->partial = FALSE;
1307 
1308  return(node);
1309 }
1310 
1311 /***********************************************************/
1314 UNIV_INTERN
1315 que_thr_t*
1317 /*==============*/
1318  que_thr_t* thr)
1319 {
1320  roll_node_t* node;
1321  ulint sig_no;
1322  trx_savept_t* savept;
1323 
1324  node = static_cast<roll_node_t *>(thr->run_node);
1325 
1326  ut_ad(que_node_get_type(node) == QUE_NODE_ROLLBACK);
1327 
1328  if (thr->prev_node == que_node_get_parent(node)) {
1329  node->state = ROLL_NODE_SEND;
1330  }
1331 
1332  if (node->state == ROLL_NODE_SEND) {
1333  mutex_enter(&kernel_mutex);
1334 
1335  node->state = ROLL_NODE_WAIT;
1336 
1337  if (node->partial) {
1338  sig_no = TRX_SIG_ROLLBACK_TO_SAVEPT;
1339  savept = &(node->savept);
1340  } else {
1341  sig_no = TRX_SIG_TOTAL_ROLLBACK;
1342  savept = NULL;
1343  }
1344 
1345  /* Send a rollback signal to the transaction */
1346 
1347  trx_sig_send(thr_get_trx(thr), sig_no, TRX_SIG_SELF, thr,
1348  savept, NULL);
1349 
1350  thr->state = QUE_THR_SIG_REPLY_WAIT;
1351 
1352  mutex_exit(&kernel_mutex);
1353 
1354  return(NULL);
1355  }
1356 
1357  ut_ad(node->state == ROLL_NODE_WAIT);
1358 
1359  thr->run_node = que_node_get_parent(node);
1360 
1361  return(thr);
1362 }