mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1221 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1221 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2006, 2011, Oracle and/or its affiliates.
 | 
						|
 | 
						|
   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 St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
#include "mariadb.h"
 | 
						|
#include "sql_priv.h"
 | 
						|
#include "unireg.h"
 | 
						|
#include "sql_base.h"                           // close_thread_tables
 | 
						|
#include "sql_parse.h"
 | 
						|
#include "event_db_repository.h"
 | 
						|
#include "key.h"                                // key_copy
 | 
						|
#include "sql_db.h"                        // get_default_db_collation
 | 
						|
#include "sql_time.h"                      // interval_type_to_name
 | 
						|
#include "tztime.h"                             // struct Time_zone
 | 
						|
#include "records.h"          // init_read_record, end_read_record
 | 
						|
#include "sp_head.h"
 | 
						|
#include "event_data_objects.h"
 | 
						|
#include "events.h"
 | 
						|
#include "sql_show.h"
 | 
						|
#include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
 | 
						|
#include "transaction.h"
 | 
						|
 | 
						|
/**
 | 
						|
  @addtogroup Event_Scheduler
 | 
						|
  @{
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
 | 
						|
{
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("db") },
 | 
						|
    { STRING_WITH_LEN("char(64)") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("name") },
 | 
						|
    { STRING_WITH_LEN("char(64)") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("body") },
 | 
						|
    { STRING_WITH_LEN("longblob") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("definer") },
 | 
						|
    { STRING_WITH_LEN("char(") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("execute_at") },
 | 
						|
    { STRING_WITH_LEN("datetime") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("interval_value") },
 | 
						|
    { STRING_WITH_LEN("int(11)") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("interval_field") },
 | 
						|
    { STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
 | 
						|
    "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
 | 
						|
    "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
 | 
						|
    "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
 | 
						|
    "'SECOND_MICROSECOND')") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("created") },
 | 
						|
    { STRING_WITH_LEN("timestamp") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("modified") },
 | 
						|
    { STRING_WITH_LEN("timestamp") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("last_executed") },
 | 
						|
    { STRING_WITH_LEN("datetime") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("starts") },
 | 
						|
    { STRING_WITH_LEN("datetime") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("ends") },
 | 
						|
    { STRING_WITH_LEN("datetime") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("status") },
 | 
						|
    { STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("on_completion") },
 | 
						|
    { STRING_WITH_LEN("enum('DROP','PRESERVE')") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("sql_mode") },
 | 
						|
    { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
 | 
						|
    "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
 | 
						|
    "'NO_UNSIGNED_SUBTRACTION',"
 | 
						|
    "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
 | 
						|
    "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
 | 
						|
    "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
 | 
						|
    "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
 | 
						|
    "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
 | 
						|
    "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
 | 
						|
    "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("comment") },
 | 
						|
    { STRING_WITH_LEN("char(64)") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("originator") },
 | 
						|
    { STRING_WITH_LEN("int(10)") },
 | 
						|
    {NULL, 0}
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("time_zone") },
 | 
						|
    { STRING_WITH_LEN("char(64)") },
 | 
						|
    { STRING_WITH_LEN("latin1") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("character_set_client") },
 | 
						|
    { STRING_WITH_LEN("char(32)") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("collation_connection") },
 | 
						|
    { STRING_WITH_LEN("char(32)") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("db_collation") },
 | 
						|
    { STRING_WITH_LEN("char(32)") },
 | 
						|
    { STRING_WITH_LEN("utf8") }
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { STRING_WITH_LEN("body_utf8") },
 | 
						|
    { STRING_WITH_LEN("longblob") },
 | 
						|
    { NULL, 0 }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
static LEX_CSTRING MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") };
 | 
						|
 | 
						|
static const TABLE_FIELD_DEF
 | 
						|
event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
 | 
						|
 | 
						|
/** In case of an error, a message is printed to the error log. */
 | 
						|
static Table_check_intact_log_error table_intact;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Puts some data common to CREATE and ALTER EVENT into a row.
 | 
						|
 | 
						|
  Used both when an event is created and when it is altered.
 | 
						|
 | 
						|
  @param   thd        THD
 | 
						|
  @param   table      The row to fill out
 | 
						|
  @param   et         Event's data
 | 
						|
  @param   sp         Event stored routine
 | 
						|
  @param   is_update  CREATE EVENT or ALTER EVENT
 | 
						|
 | 
						|
  @retval  FALSE success
 | 
						|
  @retval  TRUE error
 | 
						|
*/
 | 
						|
 | 
						|
static bool
 | 
						|
mysql_event_fill_row(THD *thd,
 | 
						|
                     TABLE *table,
 | 
						|
                     Event_parse_data *et,
 | 
						|
                     sp_head *sp,
 | 
						|
                     sql_mode_t sql_mode,
 | 
						|
                     my_bool is_update)
 | 
						|
{
 | 
						|
  CHARSET_INFO *scs= system_charset_info;
 | 
						|
  enum enum_events_table_field f_num;
 | 
						|
  Field **fields= table->field;
 | 
						|
  int rs= FALSE;
 | 
						|
 | 
						|
  DBUG_ENTER("mysql_event_fill_row");
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
 | 
						|
  DBUG_PRINT("info", ("name  =[%s]", et->name.str));
 | 
						|
 | 
						|
  DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT);
 | 
						|
 | 
						|
  if (table->s->fields < ET_FIELD_COUNT)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Safety: this can only happen if someone started the server
 | 
						|
      and then altered mysql.event.
 | 
						|
    */
 | 
						|
    my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, MYF(0),
 | 
						|
             table->s->db.str, table->alias.c_ptr(),
 | 
						|
             (int) ET_FIELD_COUNT, table->s->fields);
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (fields[f_num= ET_FIELD_DEFINER]->
 | 
						|
                              store(et->definer.str, et->definer.length, scs))
 | 
						|
    goto err_truncate;
 | 
						|
 | 
						|
  if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
 | 
						|
    goto err_truncate;
 | 
						|
 | 
						|
  if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
 | 
						|
    goto err_truncate;
 | 
						|
 | 
						|
  /* ON_COMPLETION field is NOT NULL thus not calling set_notnull()*/
 | 
						|
  rs|= fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
 | 
						|
 | 
						|
  /*
 | 
						|
    Set STATUS value unconditionally in case of CREATE EVENT.
 | 
						|
    For ALTER EVENT set it only if value of this field was changed.
 | 
						|
    Since STATUS field is NOT NULL call to set_notnull() is not needed.
 | 
						|
  */
 | 
						|
  if (!is_update || et->status_changed)
 | 
						|
    rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
 | 
						|
  rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
 | 
						|
 | 
						|
  if (!is_update)
 | 
						|
    rs|= fields[ET_FIELD_CREATED]->set_time();
 | 
						|
 | 
						|
  /*
 | 
						|
    Change the SQL_MODE only if body was present in an ALTER EVENT and of course
 | 
						|
    always during CREATE EVENT.
 | 
						|
  */
 | 
						|
  if (et->body_changed)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(sp->m_body.str);
 | 
						|
 | 
						|
    rs|= fields[ET_FIELD_SQL_MODE]->store((longlong)sql_mode, TRUE);
 | 
						|
 | 
						|
    if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
 | 
						|
                                            sp->m_body.length,
 | 
						|
                                            scs))
 | 
						|
    {
 | 
						|
      goto err_truncate;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (et->expression)
 | 
						|
  {
 | 
						|
    const String *tz_name= thd->variables.time_zone->get_name();
 | 
						|
    if (!is_update || !et->starts_null)
 | 
						|
    {
 | 
						|
      fields[ET_FIELD_TIME_ZONE]->set_notnull();
 | 
						|
      rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
 | 
						|
                                             tz_name->charset());
 | 
						|
    }
 | 
						|
 | 
						|
    fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
 | 
						|
    rs|= fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
 | 
						|
 | 
						|
    fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
 | 
						|
 | 
						|
    rs|= fields[ET_FIELD_TRANSIENT_INTERVAL]->
 | 
						|
                            store(interval_type_to_name[et->interval].str,
 | 
						|
                                  interval_type_to_name[et->interval].length,
 | 
						|
                                  scs);
 | 
						|
 | 
						|
    fields[ET_FIELD_EXECUTE_AT]->set_null();
 | 
						|
 | 
						|
    if (!et->starts_null)
 | 
						|
    {
 | 
						|
      MYSQL_TIME time;
 | 
						|
      my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->starts);
 | 
						|
 | 
						|
      fields[ET_FIELD_STARTS]->set_notnull();
 | 
						|
      fields[ET_FIELD_STARTS]->store_time(&time);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!et->ends_null)
 | 
						|
    {
 | 
						|
      MYSQL_TIME time;
 | 
						|
      my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->ends);
 | 
						|
 | 
						|
      fields[ET_FIELD_ENDS]->set_notnull();
 | 
						|
      fields[ET_FIELD_ENDS]->store_time(&time);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (et->execute_at)
 | 
						|
  {
 | 
						|
    const String *tz_name= thd->variables.time_zone->get_name();
 | 
						|
    fields[ET_FIELD_TIME_ZONE]->set_notnull();
 | 
						|
    rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
 | 
						|
                                           tz_name->charset());
 | 
						|
 | 
						|
    fields[ET_FIELD_INTERVAL_EXPR]->set_null();
 | 
						|
    fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
 | 
						|
    fields[ET_FIELD_STARTS]->set_null();
 | 
						|
    fields[ET_FIELD_ENDS]->set_null();
 | 
						|
 | 
						|
    MYSQL_TIME time;
 | 
						|
    my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->execute_at);
 | 
						|
 | 
						|
    fields[ET_FIELD_EXECUTE_AT]->set_notnull();
 | 
						|
    fields[ET_FIELD_EXECUTE_AT]->store_time(&time);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(is_update);
 | 
						|
    /*
 | 
						|
      it is normal to be here when the action is update
 | 
						|
      this is an error if the action is create. something is borked
 | 
						|
    */
 | 
						|
  }
 | 
						|
 | 
						|
  rs|= fields[ET_FIELD_MODIFIED]->set_time();
 | 
						|
 | 
						|
  if (et->comment.str)
 | 
						|
  {
 | 
						|
    if (fields[f_num= ET_FIELD_COMMENT]->
 | 
						|
                          store(et->comment.str, et->comment.length, scs))
 | 
						|
      goto err_truncate;
 | 
						|
  }
 | 
						|
 | 
						|
  fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
 | 
						|
  rs|= fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
 | 
						|
    thd->variables.character_set_client->csname,
 | 
						|
    strlen(thd->variables.character_set_client->csname),
 | 
						|
    system_charset_info);
 | 
						|
 | 
						|
  fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull();
 | 
						|
  rs|= fields[ET_FIELD_COLLATION_CONNECTION]->store(
 | 
						|
    thd->variables.collation_connection->name,
 | 
						|
    strlen(thd->variables.collation_connection->name),
 | 
						|
    system_charset_info);
 | 
						|
 | 
						|
  {
 | 
						|
    CHARSET_INFO *db_cl= get_default_db_collation(thd, et->dbname.str);
 | 
						|
 | 
						|
    fields[ET_FIELD_DB_COLLATION]->set_notnull();
 | 
						|
    rs|= fields[ET_FIELD_DB_COLLATION]->store(db_cl->name,
 | 
						|
                                              strlen(db_cl->name),
 | 
						|
                                              system_charset_info);
 | 
						|
  }
 | 
						|
 | 
						|
  if (et->body_changed)
 | 
						|
  {
 | 
						|
    fields[ET_FIELD_BODY_UTF8]->set_notnull();
 | 
						|
    rs|= fields[ET_FIELD_BODY_UTF8]->store(sp->m_body_utf8.str,
 | 
						|
                                           sp->m_body_utf8.length,
 | 
						|
                                           system_charset_info);
 | 
						|
  }
 | 
						|
 | 
						|
  if (rs)
 | 
						|
  {
 | 
						|
    my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name.str, rs);
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
 | 
						|
err_truncate:
 | 
						|
  my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name.str);
 | 
						|
  DBUG_RETURN(TRUE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Performs an index scan of event_table (mysql.event) and fills schema_table.
 | 
						|
 | 
						|
  SYNOPSIS
 | 
						|
    Event_db_repository::index_read_for_db_for_i_s()
 | 
						|
      thd          Thread
 | 
						|
      schema_table The I_S.EVENTS table
 | 
						|
      event_table  The event table to use for loading (mysql.event)
 | 
						|
      db           For which schema to do an index scan.
 | 
						|
 | 
						|
  RETURN VALUE
 | 
						|
    0  OK
 | 
						|
    1  Error
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
 | 
						|
                                               TABLE *event_table,
 | 
						|
                                               const char *db)
 | 
						|
{
 | 
						|
  CHARSET_INFO *scs= system_charset_info;
 | 
						|
  KEY *key_info;
 | 
						|
  uint key_len;
 | 
						|
  uchar *key_buf;
 | 
						|
  DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("Using prefix scanning on PK"));
 | 
						|
 | 
						|
  int ret= event_table->file->ha_index_init(0, 1);
 | 
						|
  if (ret)
 | 
						|
  {
 | 
						|
    event_table->file->print_error(ret, MYF(0));
 | 
						|
    DBUG_RETURN(true);
 | 
						|
  }
 | 
						|
 | 
						|
  key_info= event_table->key_info;
 | 
						|
 | 
						|
  if (key_info->user_defined_key_parts == 0 ||
 | 
						|
      key_info->key_part[0].field != event_table->field[ET_FIELD_DB])
 | 
						|
  {
 | 
						|
    /* Corrupted table: no index or index on a wrong column */
 | 
						|
    my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
 | 
						|
    ret= 1;
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs);
 | 
						|
  key_len= key_info->key_part[0].store_length;
 | 
						|
 | 
						|
  if (!(key_buf= (uchar *)alloc_root(thd->mem_root, key_len)))
 | 
						|
  {
 | 
						|
    /* Don't send error, it would be done by sql_alloc_error_handler() */
 | 
						|
    ret= 1;
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  key_copy(key_buf, event_table->record[0], key_info, key_len);
 | 
						|
  if (!(ret= event_table->file->ha_index_read_map(event_table->record[0],
 | 
						|
                                                  key_buf,
 | 
						|
                                                  (key_part_map)1,
 | 
						|
                                                  HA_READ_KEY_EXACT)))
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
 | 
						|
    do
 | 
						|
    {
 | 
						|
      ret= copy_event_to_schema_table(thd, schema_table, event_table);
 | 
						|
      if (ret == 0)
 | 
						|
        ret= event_table->file->ha_index_next_same(event_table->record[0],
 | 
						|
                                                   key_buf, key_len);
 | 
						|
    } while (ret == 0);
 | 
						|
  }
 | 
						|
  DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
 | 
						|
 | 
						|
  /*  ret is guaranteed to be != 0 */
 | 
						|
  if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
 | 
						|
    ret= 0;
 | 
						|
  else
 | 
						|
    event_table->file->print_error(ret, MYF(0));
 | 
						|
 | 
						|
end:
 | 
						|
  event_table->file->ha_index_end();
 | 
						|
 | 
						|
  DBUG_RETURN(MY_TEST(ret));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Performs a table scan of event_table (mysql.event) and fills schema_table.
 | 
						|
 | 
						|
  SYNOPSIS
 | 
						|
    Events_db_repository::table_scan_all_for_i_s()
 | 
						|
      thd          Thread
 | 
						|
      schema_table The I_S.EVENTS in memory table
 | 
						|
      event_table  The event table to use for loading.
 | 
						|
 | 
						|
  RETURN VALUE
 | 
						|
    FALSE  OK
 | 
						|
    TRUE   Error
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
 | 
						|
                                            TABLE *event_table)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  READ_RECORD read_record_info;
 | 
						|
  DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
 | 
						|
 | 
						|
  if (init_read_record(&read_record_info, thd, event_table, NULL, NULL, 1, 0,
 | 
						|
                       FALSE))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
 | 
						|
  /*
 | 
						|
    rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
 | 
						|
    but rr_handle_error returns -1 for that reason. Thus, read_record()
 | 
						|
    returns -1 eventually.
 | 
						|
  */
 | 
						|
  do
 | 
						|
  {
 | 
						|
    ret= read_record_info.read_record();
 | 
						|
    if (ret == 0)
 | 
						|
      ret= copy_event_to_schema_table(thd, schema_table, event_table);
 | 
						|
  } while (ret == 0);
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
 | 
						|
  end_read_record(&read_record_info);
 | 
						|
 | 
						|
  /*  ret is guaranteed to be != 0 */
 | 
						|
  DBUG_RETURN(ret == -1? FALSE:TRUE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Fills I_S.EVENTS with data loaded from mysql.event. Also used by
 | 
						|
  SHOW EVENTS
 | 
						|
 | 
						|
  The reason we reset and backup open tables here is that this
 | 
						|
  function may be called from any query that accesses
 | 
						|
  INFORMATION_SCHEMA - including a query that is issued from
 | 
						|
  a pre-locked statement, one that already has open and locked
 | 
						|
  tables.
 | 
						|
 | 
						|
  @retval FALSE  success
 | 
						|
  @retval TRUE   error
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
 | 
						|
                                        const char *db)
 | 
						|
{
 | 
						|
  TABLE *schema_table= i_s_table->table;
 | 
						|
  TABLE_LIST event_table;
 | 
						|
  int ret= 0;
 | 
						|
  DBUG_ENTER("Event_db_repository::fill_schema_events");
 | 
						|
  DBUG_PRINT("info",("db=%s", db? db:"(null)"));
 | 
						|
 | 
						|
  start_new_trans new_trans(thd);
 | 
						|
 | 
						|
  event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
 | 
						|
 | 
						|
  if (open_system_tables_for_read(thd, &event_table))
 | 
						|
  {
 | 
						|
    new_trans.restore_old_transaction();
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (table_intact.check(event_table.table, &event_table_def))
 | 
						|
  {
 | 
						|
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
 | 
						|
    ret= 1;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
 | 
						|
                     thus we won't order it. OTOH, SHOW EVENTS will be
 | 
						|
                     ordered.
 | 
						|
    2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
 | 
						|
       Reasoning: Events are per schema, therefore a scan over an index
 | 
						|
                  will save use from doing a table scan and comparing
 | 
						|
                  every single row's `db` with the schema which we show.
 | 
						|
  */
 | 
						|
  if (db)
 | 
						|
    ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
 | 
						|
  else
 | 
						|
    ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
 | 
						|
 | 
						|
err:
 | 
						|
  thd->commit_whole_transaction_and_close_tables();
 | 
						|
  new_trans.restore_old_transaction();
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("Return code=%d", ret));
 | 
						|
  DBUG_RETURN(ret);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Open mysql.event table for read.
 | 
						|
 | 
						|
  It's assumed that the caller knows what they are doing:
 | 
						|
  - whether it was necessary to reset-and-backup the open tables state
 | 
						|
  - whether the requested lock does not lead to a deadlock
 | 
						|
  - whether this open mode would work under LOCK TABLES, or inside a
 | 
						|
  stored function or trigger.
 | 
						|
 | 
						|
  Note that if the table can't be locked successfully this operation will
 | 
						|
  close it. Therefore it provides guarantee that it either opens and locks
 | 
						|
  table or fails without leaving any tables open.
 | 
						|
 | 
						|
  @param[in]  thd  Thread context
 | 
						|
  @param[in]  lock_type  How to lock the table
 | 
						|
  @param[out] table  We will store the open table here
 | 
						|
 | 
						|
  @retval TRUE open and lock failed - an error message is pushed into the
 | 
						|
               stack
 | 
						|
  @retval FALSE success
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
 | 
						|
                                      TABLE **table)
 | 
						|
{
 | 
						|
  TABLE_LIST tables;
 | 
						|
  DBUG_ENTER("Event_db_repository::open_event_table");
 | 
						|
 | 
						|
  tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type);
 | 
						|
 | 
						|
  if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
 | 
						|
  *table= tables.table;
 | 
						|
  tables.table->use_all_columns();
 | 
						|
  /* NOTE: &tables pointer will be invalid after return */
 | 
						|
  tables.table->pos_in_table_list= NULL;
 | 
						|
 | 
						|
  if (table_intact.check(*table, &event_table_def))
 | 
						|
  {
 | 
						|
    thd->commit_whole_transaction_and_close_tables();
 | 
						|
    *table= 0;                                  // Table is now closed
 | 
						|
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Creates an event record in mysql.event table.
 | 
						|
 | 
						|
  Creates an event. Relies on mysql_event_fill_row which is shared with
 | 
						|
  ::update_event.
 | 
						|
 | 
						|
  @pre All semantic checks must be performed outside. This function
 | 
						|
  only creates a record on disk.
 | 
						|
  @pre The thread handle has no open tables.
 | 
						|
 | 
						|
  @param[in,out] thd                   THD
 | 
						|
  @param[in]     parse_data            Parsed event definition
 | 
						|
  @param[in]     create_if_not         TRUE if IF NOT EXISTS clause was provided
 | 
						|
                                       to CREATE EVENT statement
 | 
						|
  @param[out]    event_already_exists  When method is completed successfully
 | 
						|
                                       set to true if event already exists else
 | 
						|
                                       set to false
 | 
						|
  @retval FALSE  success
 | 
						|
  @retval TRUE   error
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
 | 
						|
                                  bool *event_already_exists)
 | 
						|
{
 | 
						|
  int ret= 1;
 | 
						|
  TABLE *table= NULL;
 | 
						|
  sp_head *sp= thd->lex->sphead;
 | 
						|
  sql_mode_t saved_mode= thd->variables.sql_mode;
 | 
						|
  /*
 | 
						|
    Take a savepoint to release only the lock on mysql.event
 | 
						|
    table at the end but keep the global read lock and
 | 
						|
    possible other locks taken by the caller.
 | 
						|
  */
 | 
						|
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 | 
						|
 | 
						|
  DBUG_ENTER("Event_db_repository::create_event");
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("open mysql.event for update"));
 | 
						|
  DBUG_ASSERT(sp);
 | 
						|
 | 
						|
  /* Reset sql_mode during data dictionary operations. */
 | 
						|
  thd->variables.sql_mode= 0;
 | 
						|
 | 
						|
  if (open_event_table(thd, TL_WRITE, &table))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
 | 
						|
             parse_data->name.str));
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("check existence of an event with the same name"));
 | 
						|
  if (!find_named_event(&parse_data->dbname, &parse_data->name, table))
 | 
						|
  {
 | 
						|
    if (thd->lex->create_info.or_replace())
 | 
						|
    {
 | 
						|
      *event_already_exists= false; // Force the caller to update event_queue
 | 
						|
      if ((ret= table->file->ha_delete_row(table->record[0])))
 | 
						|
      {
 | 
						|
        table->file->print_error(ret, MYF(0));
 | 
						|
        goto end;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if (thd->lex->create_info.if_not_exists())
 | 
						|
    {
 | 
						|
      *event_already_exists= true;
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
 | 
						|
                          ER_EVENT_ALREADY_EXISTS,
 | 
						|
                          ER_THD(thd, ER_EVENT_ALREADY_EXISTS),
 | 
						|
                          parse_data->name.str);
 | 
						|
      ret= 0;
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
  } else
 | 
						|
    *event_already_exists= false;
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("non-existent, go forward"));
 | 
						|
 | 
						|
  restore_record(table, s->default_values);     // Get default values for fields
 | 
						|
 | 
						|
  if (check_string_char_length(&parse_data->dbname, 0,
 | 
						|
                               table->field[ET_FIELD_DB]->char_length(),
 | 
						|
                               system_charset_info, 1))
 | 
						|
  {
 | 
						|
    my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  if (check_string_char_length(&parse_data->name, 0,
 | 
						|
                               table->field[ET_FIELD_NAME]->char_length(),
 | 
						|
                               system_charset_info, 1))
 | 
						|
  {
 | 
						|
    my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
 | 
						|
  {
 | 
						|
    my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    mysql_event_fill_row() calls my_error() in case of error so no need to
 | 
						|
    handle it here
 | 
						|
  */
 | 
						|
  if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  if ((ret= table->file->ha_write_row(table->record[0])))
 | 
						|
  {
 | 
						|
    table->file->print_error(ret, MYF(0));
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
  ret= 0;
 | 
						|
 | 
						|
end:
 | 
						|
  if (table)
 | 
						|
    thd->commit_whole_transaction_and_close_tables();
 | 
						|
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 | 
						|
 | 
						|
  thd->variables.sql_mode= saved_mode;
 | 
						|
  DBUG_RETURN(MY_TEST(ret));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Used to execute ALTER EVENT. Pendant to Events::update_event().
 | 
						|
 | 
						|
  @param[in,out]  thd         thread handle
 | 
						|
  @param[in]      parse_data  parsed event definition
 | 
						|
  @param[in]      new_dbname  not NULL if ALTER EVENT RENAME
 | 
						|
                              points at a new database name
 | 
						|
  @param[in]      new_name    not NULL if ALTER EVENT RENAME
 | 
						|
                              points at a new event name
 | 
						|
 | 
						|
  @pre All semantic checks are performed outside this function,
 | 
						|
  it only updates the event definition on disk.
 | 
						|
  @pre We don't have any tables open in the given thread.
 | 
						|
 | 
						|
  @retval FALSE success
 | 
						|
  @retval TRUE error (reported)
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
 | 
						|
                                  LEX_CSTRING *new_dbname,
 | 
						|
                                  LEX_CSTRING *new_name)
 | 
						|
{
 | 
						|
  CHARSET_INFO *scs= system_charset_info;
 | 
						|
  TABLE *table= NULL;
 | 
						|
  sp_head *sp= thd->lex->sphead;
 | 
						|
  sql_mode_t saved_mode= thd->variables.sql_mode;
 | 
						|
  /*
 | 
						|
    Take a savepoint to release only the lock on mysql.event
 | 
						|
    table at the end but keep the global read lock and
 | 
						|
    possible other locks taken by the caller.
 | 
						|
  */
 | 
						|
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 | 
						|
  int ret= 1;
 | 
						|
  DBUG_ENTER("Event_db_repository::update_event");
 | 
						|
 | 
						|
  /* None or both must be set */
 | 
						|
  DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
 | 
						|
 | 
						|
  /* Reset sql_mode during data dictionary operations. */
 | 
						|
  thd->variables.sql_mode= 0;
 | 
						|
 | 
						|
  if (open_event_table(thd, TL_WRITE, &table))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
 | 
						|
  DBUG_PRINT("info", ("name: %s", parse_data->name.str));
 | 
						|
  DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
 | 
						|
 | 
						|
  /* first look whether we overwrite */
 | 
						|
  if (new_name)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
 | 
						|
    if (!find_named_event(new_dbname, new_name, table))
 | 
						|
    {
 | 
						|
      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /*
 | 
						|
    ...and then if there is such an event. Don't exchange the blocks
 | 
						|
    because you will get error 120 from table handler because new_name will
 | 
						|
    overwrite the key and SE will tell us that it cannot find the already found
 | 
						|
    row (copied into record[1] later
 | 
						|
  */
 | 
						|
  if (find_named_event(&parse_data->dbname, &parse_data->name, table))
 | 
						|
  {
 | 
						|
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  store_record(table,record[1]);
 | 
						|
 | 
						|
  /*
 | 
						|
    We check whether ALTER EVENT was given dates that are in the past.
 | 
						|
    However to know how to react, we need the ON COMPLETION type. The
 | 
						|
    check is deferred to this point because by now we have the previous
 | 
						|
    setting (from the event-table) to fall back on if nothing was specified
 | 
						|
    in the ALTER EVENT-statement.
 | 
						|
  */
 | 
						|
 | 
						|
  if (parse_data->check_dates(thd,
 | 
						|
                              (int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  /*
 | 
						|
    mysql_event_fill_row() calls my_error() in case of error so no need to
 | 
						|
    handle it here
 | 
						|
  */
 | 
						|
  if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  if (new_dbname)
 | 
						|
  {
 | 
						|
    table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs);
 | 
						|
    table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
 | 
						|
  {
 | 
						|
    table->file->print_error(ret, MYF(0));
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
  ret= 0;
 | 
						|
 | 
						|
end:
 | 
						|
  if (table)
 | 
						|
    thd->commit_whole_transaction_and_close_tables();
 | 
						|
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 | 
						|
 | 
						|
  thd->variables.sql_mode= saved_mode;
 | 
						|
  DBUG_RETURN(MY_TEST(ret));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Delete event record from mysql.event table.
 | 
						|
 | 
						|
  @param[in,out] thd            thread handle
 | 
						|
  @param[in]     db             Database name
 | 
						|
  @param[in]     name           Event name
 | 
						|
  @param[in]     drop_if_exists DROP IF EXISTS clause was specified.
 | 
						|
                                If set, and the event does not exist,
 | 
						|
                                the error is downgraded to a warning.
 | 
						|
 | 
						|
  @retval FALSE success
 | 
						|
  @retval TRUE error (reported)
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db,
 | 
						|
                                const LEX_CSTRING *name,
 | 
						|
                                bool drop_if_exists)
 | 
						|
{
 | 
						|
  TABLE *table= NULL;
 | 
						|
  /*
 | 
						|
    Take a savepoint to release only the lock on mysql.event
 | 
						|
    table at the end but keep the global read lock and
 | 
						|
    possible other locks taken by the caller.
 | 
						|
  */
 | 
						|
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 | 
						|
  int ret= 1;
 | 
						|
 | 
						|
  DBUG_ENTER("Event_db_repository::drop_event");
 | 
						|
  DBUG_PRINT("enter", ("%s@%s", db->str, name->str));
 | 
						|
 | 
						|
  if (open_event_table(thd, TL_WRITE, &table))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  if (!find_named_event(db, name, table))
 | 
						|
  {
 | 
						|
    if ((ret= table->file->ha_delete_row(table->record[0])))
 | 
						|
      table->file->print_error(ret, MYF(0));
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Event not found */
 | 
						|
  if (!drop_if_exists)
 | 
						|
  {
 | 
						|
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
 | 
						|
                      ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
 | 
						|
                      "Event", name->str);
 | 
						|
  ret= 0;
 | 
						|
 | 
						|
end:
 | 
						|
  if (table)
 | 
						|
    thd->commit_whole_transaction_and_close_tables();
 | 
						|
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 | 
						|
 | 
						|
  DBUG_RETURN(MY_TEST(ret));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Positions the internal pointer of `table` to the place where (db, name)
 | 
						|
  is stored.
 | 
						|
 | 
						|
  In case search succeeded, the table cursor points at the found row.
 | 
						|
 | 
						|
  @param[in]      db     database name
 | 
						|
  @param[in]      name   event name
 | 
						|
  @param[in,out]  table  mysql.event table
 | 
						|
 | 
						|
 | 
						|
  @retval FALSE  an event with such db/name key exists
 | 
						|
  @retval  TRUE   no record found or an error occurred.
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::find_named_event(const LEX_CSTRING *db,
 | 
						|
                                      const LEX_CSTRING *name,
 | 
						|
                                      TABLE *table)
 | 
						|
{
 | 
						|
  uchar key[MAX_KEY_LENGTH];
 | 
						|
  DBUG_ENTER("Event_db_repository::find_named_event");
 | 
						|
  DBUG_PRINT("enter", ("name: %.*s", (int) name->length, name->str));
 | 
						|
 | 
						|
  /*
 | 
						|
    Create key to find row. We have to use field->store() to be able to
 | 
						|
    handle VARCHAR and CHAR fields.
 | 
						|
    Assumption here is that the two first fields in the table are
 | 
						|
    'db' and 'name' and the first key is the primary key over the
 | 
						|
    same fields.
 | 
						|
  */
 | 
						|
  if (db->length > table->field[ET_FIELD_DB]->field_length ||
 | 
						|
      name->length > table->field[ET_FIELD_NAME]->field_length ||
 | 
						|
      table->s->keys == 0 ||
 | 
						|
      table->key_info[0].user_defined_key_parts != 2 ||
 | 
						|
      table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 ||
 | 
						|
      table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1)
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
 | 
						|
  table->field[ET_FIELD_DB]->store(db->str, db->length, &my_charset_bin);
 | 
						|
  table->field[ET_FIELD_NAME]->store(name->str, name->length, &my_charset_bin);
 | 
						|
 | 
						|
  key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
 | 
						|
 | 
						|
  if (table->file->ha_index_read_idx_map(table->record[0], 0, key,
 | 
						|
                                         HA_WHOLE_KEY,
 | 
						|
                                         HA_READ_KEY_EXACT))
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("Row not found"));
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("Row found!"));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Drops all events in the selected database, from mysql.event.
 | 
						|
 | 
						|
  SYNOPSIS
 | 
						|
    Event_db_repository::drop_schema_events()
 | 
						|
      thd     Thread
 | 
						|
      schema  The database to clean from events
 | 
						|
*/
 | 
						|
 | 
						|
void
 | 
						|
Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
 | 
						|
{
 | 
						|
  int ret= 0;
 | 
						|
  TABLE *table= NULL;
 | 
						|
  READ_RECORD read_record_info;
 | 
						|
  enum enum_events_table_field field= ET_FIELD_DB;
 | 
						|
  DBUG_ENTER("Event_db_repository::drop_schema_events");
 | 
						|
  DBUG_PRINT("enter", ("field: %d  schema: %s", field, schema->str));
 | 
						|
 | 
						|
  start_new_trans new_trans(thd);
 | 
						|
 | 
						|
  if (open_event_table(thd, TL_WRITE, &table))
 | 
						|
  {
 | 
						|
    new_trans.restore_old_transaction();
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
  }
 | 
						|
 | 
						|
  /* only enabled events are in memory, so we go now and delete the rest */
 | 
						|
  if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  while (!ret && !(read_record_info.read_record()))
 | 
						|
  {
 | 
						|
    char *et_field= get_field(thd->mem_root, table->field[field]);
 | 
						|
 | 
						|
    /* et_field may be NULL if the table is corrupted or out of memory */
 | 
						|
    if (et_field)
 | 
						|
    {
 | 
						|
      LEX_CSTRING et_field_lex= { et_field, strlen(et_field) };
 | 
						|
      DBUG_PRINT("info", ("Current event %s name=%s", et_field,
 | 
						|
                          get_field(thd->mem_root,
 | 
						|
                                    table->field[ET_FIELD_NAME])));
 | 
						|
 | 
						|
      if (!sortcmp_lex_string(&et_field_lex, schema, system_charset_info))
 | 
						|
      {
 | 
						|
        DBUG_PRINT("info", ("Dropping"));
 | 
						|
        if ((ret= table->file->ha_delete_row(table->record[0])))
 | 
						|
          table->file->print_error(ret, MYF(0));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  end_read_record(&read_record_info);
 | 
						|
 | 
						|
end:
 | 
						|
  thd->commit_whole_transaction_and_close_tables();
 | 
						|
  new_trans.restore_old_transaction();
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Looks for a named event in mysql.event and then loads it from
 | 
						|
  the table.
 | 
						|
 | 
						|
  @pre The given thread does not have open tables.
 | 
						|
 | 
						|
  @retval FALSE  success
 | 
						|
  @retval TRUE   error
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
 | 
						|
                                      const LEX_CSTRING *name,
 | 
						|
                                      Event_basic *etn)
 | 
						|
{
 | 
						|
  bool ret;
 | 
						|
  TABLE_LIST event_table;
 | 
						|
  DBUG_ENTER("Event_db_repository::load_named_event");
 | 
						|
  DBUG_PRINT("enter",("thd: %p  name: %*s", thd,
 | 
						|
                      (int) name->length, name->str));
 | 
						|
 | 
						|
  start_new_trans new_trans(thd);
 | 
						|
  /* Reset sql_mode during data dictionary operations. */
 | 
						|
  Sql_mode_instant_set sms(thd, 0);
 | 
						|
 | 
						|
  event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
 | 
						|
 | 
						|
  /*
 | 
						|
    We don't use open_event_table() here to make sure that SHOW
 | 
						|
    CREATE EVENT works properly in transactional context, and
 | 
						|
    does not release transactional metadata locks when the
 | 
						|
    event table is closed.
 | 
						|
  */
 | 
						|
  if (!(ret= open_system_tables_for_read(thd, &event_table)))
 | 
						|
  {
 | 
						|
    if (table_intact.check(event_table.table, &event_table_def))
 | 
						|
    {
 | 
						|
      thd->commit_whole_transaction_and_close_tables();
 | 
						|
      new_trans.restore_old_transaction();
 | 
						|
      my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
    }
 | 
						|
 | 
						|
    if ((ret= find_named_event(dbname, name, event_table.table)))
 | 
						|
      my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
 | 
						|
    else if ((ret= etn->load_from_row(thd, event_table.table)))
 | 
						|
      my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
 | 
						|
    thd->commit_whole_transaction_and_close_tables();
 | 
						|
  }
 | 
						|
  new_trans.restore_old_transaction();
 | 
						|
 | 
						|
  DBUG_RETURN(ret);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Update the event record in mysql.event table with a changed status
 | 
						|
  and/or last execution time.
 | 
						|
 | 
						|
  @pre The thread handle does not have open tables.
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::
 | 
						|
update_timing_fields_for_event(THD *thd,
 | 
						|
                               const LEX_CSTRING *event_db_name,
 | 
						|
                               const LEX_CSTRING *event_name,
 | 
						|
                               my_time_t last_executed,
 | 
						|
                               ulonglong status)
 | 
						|
{
 | 
						|
  TABLE *table= NULL;
 | 
						|
  Field **fields;
 | 
						|
  int ret= 1;
 | 
						|
  MYSQL_TIME time;
 | 
						|
  DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
 | 
						|
 | 
						|
  DBUG_ASSERT(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY);
 | 
						|
 | 
						|
  /*
 | 
						|
    Take a savepoint to release only the lock on mysql.event
 | 
						|
    table at the end but keep the global read lock and
 | 
						|
    possible other locks taken by the caller.
 | 
						|
  */
 | 
						|
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 | 
						|
  if (open_event_table(thd, TL_WRITE, &table))
 | 
						|
    DBUG_RETURN(1);
 | 
						|
 | 
						|
  fields= table->field;
 | 
						|
  /*
 | 
						|
    Turn off row binlogging of event timing updates. These are not used
 | 
						|
    for RBR of events replicated to the slave.
 | 
						|
  */
 | 
						|
  table->file->row_logging= 0;
 | 
						|
 | 
						|
  if (find_named_event(event_db_name, event_name, table))
 | 
						|
    goto end;
 | 
						|
 | 
						|
  store_record(table, record[1]);
 | 
						|
 | 
						|
  my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
 | 
						|
  fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
 | 
						|
  fields[ET_FIELD_LAST_EXECUTED]->store_time(&time);
 | 
						|
 | 
						|
  fields[ET_FIELD_STATUS]->set_notnull();
 | 
						|
  fields[ET_FIELD_STATUS]->store(status, TRUE);
 | 
						|
 | 
						|
  if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
 | 
						|
  {
 | 
						|
    table->file->print_error(ret, MYF(0));
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  ret= 0;
 | 
						|
end:
 | 
						|
  if (thd->commit_whole_transaction_and_close_tables())
 | 
						|
    ret= 1;
 | 
						|
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 | 
						|
 | 
						|
  DBUG_RETURN(MY_TEST(ret));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Open mysql.db, mysql.user and mysql.event and check whether:
 | 
						|
    - mysql.db exists and is up to date (or from a newer version of MySQL),
 | 
						|
    - mysql.user has column Event_priv at an expected position,
 | 
						|
    - mysql.event exists and is up to date (or from a newer version of
 | 
						|
      MySQL)
 | 
						|
 | 
						|
  This function is called only when the server is started.
 | 
						|
  @pre The passed in thread handle has no open tables.
 | 
						|
 | 
						|
  @retval FALSE  OK
 | 
						|
  @retval TRUE   Error, an error message is output to the error log.
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Event_db_repository::check_system_tables(THD *thd)
 | 
						|
{
 | 
						|
  TABLE_LIST tables;
 | 
						|
  int ret= FALSE;
 | 
						|
  DBUG_ENTER("Event_db_repository::check_system_tables");
 | 
						|
  DBUG_PRINT("enter", ("thd: %p", thd));
 | 
						|
 | 
						|
  /* Check mysql.event */
 | 
						|
  tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
 | 
						|
 | 
						|
  if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
 | 
						|
  {
 | 
						|
    ret= 1;
 | 
						|
    sql_print_error("Cannot open mysql.event");
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (table_intact.check(tables.table, &event_table_def))
 | 
						|
      ret= 1;
 | 
						|
    close_mysql_tables(thd);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(MY_TEST(ret));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  @} (End of group Event_Scheduler)
 | 
						|
*/
 |