/* Copyright (C) 2006,2007,2008 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */

/*
  Q: Why isn't ma_recovery_util.c simply moved to ma_recovery.c ?

  A: ma_recovery.c, because it invokes objects from ma_check.c (like
  maria_chk_init()) causes the following problem:
  if a source file a.c of a program invokes a function defined in
  ma_recovery.c, then a.o depends on ma_recovery.o which depends on
  ma_check.o: linker thus brings in ma_check.o. That brings in the
  dependencies of ma_check.o which are definitions of _ma_check_print_info()
  etc; if a.o does not define them then the ones of ha_maria.o are used
  i.e. ha_maria.o is linked into the program, and this brings in dependencies
  of ha_maria.o on mysqld.o into the program's linking which thus fails, as
  the program is not linked with mysqld.o.
  Thus, while several functions defined in ma_recovery.c could be useful to
  other files, they cannot be used by them.
  So we are going to gradually move a great share of ma_recovery.c's exported
  functions into the present file, to isolate the problematic components and
  avoid the problem.
*/

#include "maria_def.h"

HASH all_dirty_pages;
struct st_dirty_page /* used only in the REDO phase */
{
  uint64 file_and_page_id;
  LSN rec_lsn;
};
/*
  LSN after which dirty pages list does not apply. Can be slightly before
  when ma_checkpoint_execute() started.
*/
LSN checkpoint_start= LSN_IMPOSSIBLE;

/** @todo looks like duplicate of recovery_message_printed */
my_bool procent_printed;
FILE *tracef; /**< trace file for debugging */

ulong recovery_found_crashed_tables;
uint skipped_lsn_err_count;

/** @brief Prints to a trace file if it is not NULL */
void tprint(FILE *trace_file __attribute__ ((unused)),
            const char *format __attribute__ ((unused)), ...)
{
  va_list args;
#ifndef DBUG_OFF
  {
    char buff[1024];
    size_t length;
    va_start(args, format);
    length= my_vsnprintf(buff, sizeof(buff)-1, format, args);
    if (length && buff[length-1] == '\n')
      buff[length-1]= 0;                      /* Don't print end \n */
    DBUG_PRINT("info", ("%s", buff));
    va_end(args);
  }
#endif
  va_start(args, format);
  if (trace_file != NULL)
    vfprintf(trace_file, format, args);
  va_end(args);
}


void eprint(FILE *trace_file __attribute__ ((unused)),
            const char *format __attribute__ ((unused)), ...)
{
  va_list args;
  va_start(args, format);
  DBUG_PRINT("error", ("%s", format));
  if (!trace_file)
    trace_file= stderr;

  if (procent_printed)
  {
    procent_printed= 0;
    /* In silent mode, print on another line than the 0% 10% 20% line */
    fputc('\n', stderr);
    fflush(stderr);
  }
  vfprintf(trace_file , format, args);
  fputc('\n', trace_file);
  if (trace_file != stderr)
  {
    va_start(args, format);
    my_printv_error(HA_ERR_INITIALIZATION, format, MYF(0), args);
  }
  va_end(args);
  fflush(trace_file);
}


/**
   Tells if the dirty pages list found in checkpoint record allows to ignore a
   REDO for a certain page.

   @param  shortid         short id of the table
   @param  lsn             REDO record's LSN
   @param  page            page number
   @param  index           TRUE if index page, FALSE if data page
*/

my_bool _ma_redo_not_needed_for_page(uint16 shortid, LSN lsn,
                                     pgcache_page_no_t page,
                                     my_bool index)
{
  if (cmp_translog_addr(lsn, checkpoint_start) < 0)
  {
    /*
      64-bit key is formed like this:
      Most significant byte: 0 if data page, 1 if index page
      Next 2 bytes: table's short id
      Next 5 bytes: page number
    */
    char llbuf[22];
    uint64 file_and_page_id=
      (((uint64)((index << 16) | shortid)) << 40) | page;
    struct st_dirty_page *dirty_page= (struct st_dirty_page *)
      my_hash_search(&all_dirty_pages,
                  (uchar *)&file_and_page_id, sizeof(file_and_page_id));
    DBUG_PRINT("info", ("page %lld in dirty pages list: %d",
                        (ulonglong) page,
                        dirty_page != NULL));
    if ((dirty_page == NULL) ||
        cmp_translog_addr(lsn, dirty_page->rec_lsn) < 0)
    {
      tprint(tracef, ", ignoring page %s because of dirty_pages list\n",
             llstr((ulonglong) page, llbuf));
      return TRUE;
    }
  }
  return FALSE;
}
