/*
   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.

   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,
   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */

#include "transaction_info.h"
#include "mysys_err.h"
#include "sql_class.h"

Transaction_ctx::Transaction_ctx()
{
  memset(this, 0, sizeof(*this));
  init_sql_alloc(key_memory_thd_transactions, &m_mem_root,
                 global_system_variables.trans_alloc_block_size,
                 global_system_variables.trans_prealloc_size);
}


void Transaction_ctx::push_unsafe_rollback_warnings(THD *thd)
{
  if (m_scope_info[SESSION].has_modified_non_trans_table())
    push_warning(thd, Sql_condition::SL_WARNING,
                 ER_WARNING_NOT_COMPLETE_ROLLBACK,
                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));

  if (m_scope_info[SESSION].has_created_temp_table())
    push_warning(thd, Sql_condition::SL_WARNING,
                 ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE,
                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE));

  if (m_scope_info[SESSION].has_dropped_temp_table())
    push_warning(thd, Sql_condition::SL_WARNING,
                 ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE,
                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE));
}


bool Transaction_ctx::add_changed_table(const char *key, long key_length)
{
  DBUG_ENTER("Transaction_ctx::add_changed_table");
  CHANGED_TABLE_LIST **prev_changed = &m_changed_tables;
  CHANGED_TABLE_LIST *curr = m_changed_tables;

  for (; curr; prev_changed = &(curr->next), curr = curr->next)
  {
    int cmp =  (long)curr->key_length - (long)key_length;
    if (cmp < 0)
    {
      if (list_include(prev_changed, curr,
                       changed_table_dup(key, key_length)))
        DBUG_RETURN(true);

      DBUG_PRINT("info",
                 ("key_length: %ld  %u", key_length,
                     (*prev_changed)->key_length));
      DBUG_RETURN(false);
    }
    else if (cmp == 0)
    {
      cmp = memcmp(curr->key, key, curr->key_length);
      if (cmp < 0)
      {
        if (list_include(prev_changed, curr, changed_table_dup(key, key_length)))
          DBUG_RETURN(true);

        DBUG_PRINT("info",
                   ("key_length:  %ld  %u", key_length,
                       (*prev_changed)->key_length));
        DBUG_RETURN(false);
      }
      else if (cmp == 0)
      {
        DBUG_PRINT("info", ("already in list"));
        DBUG_RETURN(false);
        DBUG_RETURN(false);
      }
    }
  }
  *prev_changed = changed_table_dup(key, key_length);
  if (!(*prev_changed))
    DBUG_RETURN(true);
  DBUG_PRINT("info", ("key_length: %ld  %u", key_length,
      (*prev_changed)->key_length));
  DBUG_RETURN(false);
}


CHANGED_TABLE_LIST* Transaction_ctx::changed_table_dup(const char *key,
                                                       long key_length)
{
  CHANGED_TABLE_LIST* new_table =
      (CHANGED_TABLE_LIST*) allocate_memory(
          ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST)) + key_length + 1);
  if (!new_table)
  {
    my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_FATALERROR),
             ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
    return 0;
  }

  new_table->key= ((char*)new_table)+ ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST));
  new_table->next = 0;
  new_table->key_length = key_length;
  ::memcpy(new_table->key, key, key_length);
  return new_table;
}
