/**
The handler class is the interface for dynamically loadable
storage engines. Do not add ifdefs and take care when adding or
changing virtual functions to avoid vtable confusion
*/ class handler :public Sql_alloc
{
public:
typedef ulonglong Table_flags;
protected:
TABLE_SHARE *table_share; /* The table definition */
TABLE *table; /* The current open table */
Table_flags cached_table_flags; /* Set on init() and open() */ ha_rows estimation_rows_to_insert;
public:
handlerton *ht; /* storage engine of this handler */
uchar *ref; /* Pointer to current row */
uchar *dup_ref; /* Pointer to duplicate row */ ha_statistics stats; /** The following are for read_multi_range */
bool multi_range_sorted;
KEY_MULTI_RANGE *multi_range_curr;
KEY_MULTI_RANGE *multi_range_end;
HANDLER_BUFFER *multi_range_buffer; /** The following are for read_range() */
key_range save_end_range, *end_range;
KEY_PART_INFO *range_key_part;
int key_compare_result_on_equal;
bool eq_range; uint errkey; /* Last dup key */
uint key_used_on_scan;
uint active_index;
/** Length of ref (1-8 or the clustered key length) */
uint ref_length;
FT_INFO *ft_handler;
enum {NONE=, INDEX, RND} inited;
bool locked;
bool implicit_emptied; /* Can be !=0 only if HEAP */
const COND *pushed_cond;
/**
next_insert_id is the next value which should be inserted into the
auto_increment column: in a inserting-multi-row statement (like INSERT
SELECT), for the first row where the autoinc value is not specified by the
statement, get_auto_increment() called and asked to generate a value,
next_insert_id is set to the next value, then for all other rows
next_insert_id is used (and increased each time) without calling
get_auto_increment().
*/
ulonglong next_insert_id;
/**
insert id for the current row (*autogenerated*; if not
autogenerated, it's 0).
At first successful insertion, this variable is stored into
THD::first_successful_insert_id_in_cur_stmt.
*/
ulonglong insert_id_for_cur_row;
/**
Interval returned by get_auto_increment() and being consumed by the
inserter.
*/
Discrete_interval auto_inc_interval_for_cur_row;
/**
Number of reserved auto-increment intervals. Serves as a heuristic
when we have no estimation of how many records the statement will insert:
the more intervals we have reserved, the bigger the next one. Reset in
handler::ha_release_auto_increment().
*/
uint auto_inc_intervals_count; /**
Instrumented table associated with this handler.
This member should be set to NULL when no instrumentation is in place,
so that linking an instrumented/non instrumented server/plugin works.
For example:
- the server is compiled with the instrumentation.
The server expects either NULL or valid pointers in m_psi.
- an engine plugin is compiled without instrumentation.
The plugin can not leave this pointer uninitialized,
or can not leave a trash value on purpose in this pointer,
as this would crash the server.
*/
PSI_table *m_psi; handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(),
estimation_rows_to_insert(), ht(ht_arg),
ref(), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
ref_length(sizeof(my_off_t)),
ft_handler(), inited(NONE),
locked(FALSE), implicit_emptied(),
pushed_cond(), next_insert_id(), insert_id_for_cur_row(),
auto_inc_intervals_count(),
m_psi(NULL)
{}
virtual ~handler(void)
{
DBUG_ASSERT(locked == FALSE);
DBUG_ASSERT(inited == NONE);
}
virtual handler *clone(const char *name, MEM_ROOT *mem_root);
/** This is called after create to allow us to set up cached variables */
void init()
{
cached_table_flags= table_flags();
}
/* ha_ methods: pubilc wrappers for private virtual API */ int ha_open(TABLE *table, const char *name, int mode, int test_if_locked);
int ha_index_init(uint idx, bool sorted)
{
DBUG_EXECUTE_IF("ha_index_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
int result;
DBUG_ENTER("ha_index_init");
DBUG_ASSERT(inited==NONE);
if (!(result= index_init(idx, sorted)))
inited=INDEX;
DBUG_RETURN(result);
}
int ha_index_end()
{
DBUG_ENTER("ha_index_end");
DBUG_ASSERT(inited==INDEX);
inited=NONE;
DBUG_RETURN(index_end());
}
int ha_rnd_init(bool scan)
{
DBUG_EXECUTE_IF("ha_rnd_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
int result;
DBUG_ENTER("ha_rnd_init");
DBUG_ASSERT(inited==NONE || (inited==RND && scan));
inited= (result= rnd_init(scan)) ? NONE: RND;
DBUG_RETURN(result);
}
int ha_rnd_end()
{
DBUG_ENTER("ha_rnd_end");
DBUG_ASSERT(inited==RND);
inited=NONE;
DBUG_RETURN(rnd_end());
}
int ha_reset();
/* this is necessary in many places, e.g. in HANDLER command */
int ha_index_or_rnd_end()
{
return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : ;
}
/**
The cached_table_flags is set at ha_open and ha_external_lock
*/
Table_flags ha_table_flags() const { return cached_table_flags; }
/**
These functions represent the public interface to *users* of the
handler class, hence they are *not* virtual. For the inheritance
interface, see the (private) functions write_row(), update_row(),
and delete_row() below.
*/
int ha_external_lock(THD *thd, int lock_type);
int ha_write_row(uchar * buf);
int ha_update_row(const uchar * old_data, uchar * new_data);
int ha_delete_row(const uchar * buf);
void ha_release_auto_increment(); int check_collation_compatibility();
int ha_check_for_upgrade(HA_CHECK_OPT *check_opt);
/** to be actually called to get 'check()' functionality*/
int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
int ha_repair(THD* thd, HA_CHECK_OPT* check_opt);
void ha_start_bulk_insert(ha_rows rows)
{
estimation_rows_to_insert= rows;
start_bulk_insert(rows);
}
int ha_end_bulk_insert()
{
estimation_rows_to_insert= ;
return end_bulk_insert();
}
int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
uint *dup_key_found);
int ha_delete_all_rows();
int ha_truncate();
int ha_reset_auto_increment(ulonglong value);
int ha_optimize(THD* thd, HA_CHECK_OPT* check_opt);
int ha_analyze(THD* thd, HA_CHECK_OPT* check_opt);
bool ha_check_and_repair(THD *thd);
int ha_disable_indexes(uint mode);
int ha_enable_indexes(uint mode);
int ha_discard_or_import_tablespace(my_bool discard);
void ha_prepare_for_alter();
int ha_rename_table(const char *from, const char *to);
int ha_delete_table(const char *name);
void ha_drop_table(const char *name); int ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info); int ha_create_handler_files(const char *name, const char *old_name,
int action_flag, HA_CREATE_INFO *info); int ha_change_partitions(HA_CREATE_INFO *create_info,
const char *path,
ulonglong * const copied,
ulonglong * const deleted,
const uchar *pack_frm_data,
size_t pack_frm_len);
int ha_drop_partitions(const char *path);
int ha_rename_partitions(const char *path); void adjust_next_insert_id_after_explicit_value(ulonglong nr);
int update_auto_increment();
void print_keydup_error(uint key_nr, const char *msg);
virtual void print_error(int error, myf errflag);
virtual bool get_error_message(int error, String *buf);
uint get_dup_key(int error);
virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
{
table= table_arg;
table_share= share;
}
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + ; }
virtual double read_time(uint index, uint ranges, ha_rows rows)
{ return rows2double(ranges+rows); }
virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }
bool has_transactions()
{ return (ha_table_flags() & HA_NO_TRANSACTIONS) == ; }
virtual uint extra_rec_buf_length() const { return ; } /**
This method is used to analyse the error to see whether the error
is ignorable or not, certain handlers can have more error that are
ignorable than others. E.g. the partition handler can get inserts
into a range where there is no partition and this is an ignorable
error.
HA_ERR_FOUND_DUP_UNIQUE is a special case in MyISAM that means the
same thing as HA_ERR_FOUND_DUP_KEY but can in some cases lead to
a slightly different error message.
*/
virtual bool is_fatal_error(int error, uint flags)
{
if (!error ||
((flags & HA_CHECK_DUP_KEY) &&
(error == HA_ERR_FOUND_DUPP_KEY ||
error == HA_ERR_FOUND_DUPP_UNIQUE)))
return FALSE;
return TRUE;
} /**
Number of rows in table. It will only be called if
(table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
*/
virtual ha_rows records() { return stats.records; }
/**
Return upper bound of current number of records in the table
(max. of how many records one will retrieve when doing a full table scan)
If upper bound is not known, HA_POS_ERROR should be returned as a max
possible upper bound.
*/
virtual ha_rows estimate_rows_upper_bound()
{ return stats.records+EXTRA_RECORDS; } /**
Get the row type from the storage engine. If this method returns
ROW_TYPE_NOT_USED, the information in HA_CREATE_INFO should be used.
*/
virtual enum row_type get_row_type() const { return ROW_TYPE_NOT_USED; } virtual const char *index_type(uint key_number) { DBUG_ASSERT(); return "";} /**
Signal that the table->read_set and table->write_set table maps changed
The handler is allowed to set additional bits in the above map in this
call. Normally the handler should ignore all calls until we have done
a ha_rnd_init() or ha_index_init(), write_row(), update_row or delete_row()
as there may be several calls to this routine.
*/
virtual void column_bitmaps_signal();
uint get_index(void) const { return active_index; }
virtual int close(void)=; /**
@retval 0 Bulk update used by handler
@retval 1 Bulk update not used, normal operation used
*/
virtual bool start_bulk_update() { return ; }
/**
@retval 0 Bulk delete used by handler
@retval 1 Bulk delete not used, normal operation used
*/
virtual bool start_bulk_delete() { return ; }
/**
After this call all outstanding updates must be performed. The number
of duplicate key errors are reported in the duplicate key parameter.
It is allowed to continue to the batched update after this call, the
handler has to wait until end_bulk_update with changing state. @param dup_key_found Number of duplicate keys found @retval 0 Success
@retval >0 Error code
*/
virtual int exec_bulk_update(uint *dup_key_found)
{
DBUG_ASSERT(FALSE);
return HA_ERR_WRONG_COMMAND;
}
/**
Perform any needed clean-up, no outstanding updates are there at the
moment.
*/
virtual void end_bulk_update() { return; }
/**
Execute all outstanding deletes and close down the bulk delete. @retval 0 Success
@retval >0 Error code
*/
virtual int end_bulk_delete()
{
DBUG_ASSERT(FALSE);
return HA_ERR_WRONG_COMMAND;
}
/**
@brief
Positions an index cursor to the index specified in the handle. Fetches the
row if available. If the key value is null, begin at the first key of the
index.
*/
virtual int index_read_map(uchar * buf, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
uint key_len= calculate_key_len(table, active_index, key, keypart_map);
return index_read(buf, key, key_len, find_flag);
}
/**
@brief
Positions an index cursor to the index specified in the handle. Fetches the
row if available. If the key value is null, begin at the first key of the
index.
*/
virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag);
virtual int index_next(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_prev(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_first(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_last(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_next_same(uchar *buf, const uchar *key, uint keylen);
/**
@brief
The following functions works like index_read, but it find the last
row with the current key value or prefix.
*/
virtual int index_read_last_map(uchar * buf, const uchar * key,
key_part_map keypart_map)
{
uint key_len= calculate_key_len(table, active_index, key, keypart_map);
return index_read_last(buf, key, key_len);
}
virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
KEY_MULTI_RANGE *ranges, uint range_count,
bool sorted, HANDLER_BUFFER *buffer);
virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
virtual int read_range_first(const key_range *start_key,
const key_range *end_key,
bool eq_range, bool sorted);
virtual int read_range_next();
int compare_key(key_range *range);
virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
void ft_end() { ft_handler=NULL; }
virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
{ return NULL; }
virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; }
virtual int rnd_next(uchar *buf)=0;
virtual int rnd_pos(uchar * buf, uchar *pos)=;
/**
This function only works for handlers having
HA_PRIMARY_KEY_REQUIRED_FOR_POSITION set.
It will return the row with the PK given in the record argument.
*/
virtual int rnd_pos_by_record(uchar *record)
{
position(record);
return rnd_pos(record, ref);
}
virtual int read_first_row(uchar *buf, uint primary_key);
/**
The following function is only needed for tables that may be temporary
tables during joins.
*/
virtual int restart_rnd_next(uchar *buf, uchar *pos)
{ return HA_ERR_WRONG_COMMAND; }
virtual int rnd_same(uchar *buf, uint inx)
{ return HA_ERR_WRONG_COMMAND; }
virtual ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key)
{ return (ha_rows) ; }
/*
If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, then it sets ref
(reference to the row, aka position, with the primary key given in
the record).
Otherwise it set ref to the current row.
*/
virtual void position(const uchar *record)=;
virtual int info(uint)=; // see my_base.h for full description
virtual void get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id);
virtual int extra(enum ha_extra_function operation)
{ return ; }
virtual int extra_opt(enum ha_extra_function operation, ulong cache_size)
{ return extra(operation); } /**
In an UPDATE or DELETE, if the row under the cursor was locked by another
transaction, and the engine used an optimistic read of the last
committed row value under the cursor, then the engine returns 1 from this
function. MySQL must NOT try to update this optimistic value. If the
optimistic value does not match the WHERE condition, MySQL can decide to
skip over this row. Currently only works for InnoDB. This can be used to
avoid unnecessary lock waits. If this method returns nonzero, it will also signal the storage
engine that the next read will be a locking re-read of the row.
*/
virtual bool was_semi_consistent_read() { return ; }
/**
Tell the engine whether it should avoid unnecessary lock waits.
If yes, in an UPDATE or DELETE, if the row under the cursor was locked
by another transaction, the engine may try an optimistic read of
the last committed row value under the cursor.
*/
virtual void try_semi_consistent_read(bool) {}
virtual void unlock_row() {}
virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return ;}
virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
void set_next_insert_id(ulonglong id)
{
DBUG_PRINT("info",("auto_increment: next value %lu", (ulong)id));
next_insert_id= id;
}
void restore_auto_increment(ulonglong prev_insert_id)
{
/*
Insertion of a row failed, re-use the lastly generated auto_increment
id, for the next row. This is achieved by resetting next_insert_id to
what it was before the failed insertion (that old value is provided by
the caller). If that value was 0, it was the first row of the INSERT;
then if insert_id_for_cur_row contains 0 it means no id was generated
for this first row, so no id was generated since the INSERT started, so
we should set next_insert_id to 0; if insert_id_for_cur_row is not 0, it
is the generated id of the first and failed row, so we use it.
*/
next_insert_id= (prev_insert_id > ) ? prev_insert_id :
insert_id_for_cur_row;
} virtual void update_create_info(HA_CREATE_INFO *create_info) {}
int check_old_types();
virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
/* end of the list of admin commands */ virtual int indexes_are_disabled(void) {return ;}
virtual char *update_table_comment(const char * comment)
{ return (char*) comment;}
virtual void append_create_info(String *packet) {}
/**
If index == MAX_KEY then a check for table is made and if index <
MAX_KEY then a check is made if the table has foreign keys and if
a foreign key uses this index (and thus the index cannot be dropped). @param index Index to check if foreign key uses it @retval TRUE Foreign key defined on table or index
@retval FALSE No foreign key defined
*/
virtual bool is_fk_defined_on_table_or_index(uint index)
{ return FALSE; }
virtual char* get_foreign_key_create_info()
{ return(NULL);} /* gets foreign key create string from InnoDB */
virtual char* get_tablespace_name(THD *thd, char *name, uint name_len)
{ return(NULL);} /* gets tablespace name from handler */
/** used in ALTER TABLE; 1 if changing storage engine is allowed */
virtual bool can_switch_engines() { return ; }
/**
Get the list of foreign keys in this table. @remark Returns the set of foreign keys where this table is the
dependent or child table. @param thd The thread handle.
@param f_key_list[out] The list of foreign keys. @return The handler error code or zero for success.
*/
virtual int
get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
{ return ; }
/**
Get the list of foreign keys referencing this table. @remark Returns the set of foreign keys where this table is the
referenced or parent table. @param thd The thread handle.
@param f_key_list[out] The list of foreign keys. @return The handler error code or zero for success.
*/
virtual int
get_parent_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
{ return ; }
virtual uint referenced_by_foreign_key() { return ;}
virtual void init_table_handle_for_HANDLER()
{ return; } /* prepare InnoDB for HANDLER */
virtual void free_foreign_key_create_info(char* str) {}
/** The following can be called without an open handler */
virtual const char *table_type() const =;
/**
If frm_error() is called then we will use this to find out what file
extentions exist for the storage engine. This is also used by the default
rename_table and delete_table method in handler.cc. For engines that have two file name extentions (separate meta/index file
and data file), the order of elements is relevant. First element of engine
file name extentions array should be meta/index file extention. Second
element - data file extention. This order is assumed by
prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
*/
virtual const char **bas_ext() const =; virtual int get_default_no_partitions(HA_CREATE_INFO *info) { return ;}
virtual void set_auto_partitions(partition_info *part_info) { return; }
virtual bool get_no_parts(const char *name,
uint *no_parts)
{
*no_parts= ;
return ;
}
virtual void set_part_info(partition_info *part_info) {return;} virtual ulong index_flags(uint idx, uint part, bool all_parts) const =; /**
First phase of in-place add index.
Handlers are supposed to create new indexes here but not make them
visible. @param table_arg Table to add index to
@param key_info Information about new indexes
@param num_of_key Number of new indexes
@param add[out] Context of handler specific information needed
for final_add_index(). @note This function can be called with less than exclusive metadata
lock depending on which flags are listed in alter_table_flags.
*/
virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
handler_add_index **add)
{ return (HA_ERR_WRONG_COMMAND); } /**
Second and last phase of in-place add index.
Commit or rollback pending new indexes. @param add Context of handler specific information from add_index().
@param commit If true, commit. If false, rollback index changes. @note This function is called with exclusive metadata lock.
*/
virtual int final_add_index(handler_add_index *add, bool commit)
{ return (HA_ERR_WRONG_COMMAND); } virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
uint num_of_keys)
{ return (HA_ERR_WRONG_COMMAND); }
virtual int final_drop_index(TABLE *table_arg)
{ return (HA_ERR_WRONG_COMMAND); } uint max_record_length() const
{ return min(HA_MAX_REC_LENGTH, max_supported_record_length()); }
uint max_keys() const
{ return min(MAX_KEY, max_supported_keys()); }
uint max_key_parts() const
{ return min(MAX_REF_PARTS, max_supported_key_parts()); }
uint max_key_length() const
{ return min(MAX_KEY_LENGTH, max_supported_key_length()); }
uint max_key_part_length() const
{ return min(MAX_KEY_LENGTH, max_supported_key_part_length()); } virtual uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
virtual uint max_supported_keys() const { return ; }
virtual uint max_supported_key_parts() const { return MAX_REF_PARTS; }
virtual uint max_supported_key_length() const { return MAX_KEY_LENGTH; }
virtual uint max_supported_key_part_length() const { return ; }
virtual uint min_record_length(uint options) const { return ; } virtual bool low_byte_first() const { return ; }
virtual uint checksum() const { return ; }
virtual bool is_crashed() const { return ; }
virtual bool auto_repair() const { return ; } #define CHF_CREATE_FLAG 0
#define CHF_DELETE_FLAG 1
#define CHF_RENAME_FLAG 2
#define CHF_INDEX_FLAG 3 /**
@note lock_count() can return > 1 if the table is MERGE or partitioned.
*/
virtual uint lock_count(void) const { return ; }
/**
Is not invoked for non-transactional temporary tables. @note store_lock() can return more than one lock if the table is MERGE
or partitioned. @note that one can NOT rely on table->in_use in store_lock(). It may
refer to a different thread if called from mysql_lock_abort_for_thread(). @note If the table is MERGE, store_lock() can return less locks
than lock_count() claimed. This can happen when the MERGE children
are not attached when this is called from another thread.
*/
virtual THR_LOCK_DATA **store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)=; /** Type of table for caching query */
virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } /**
@brief Register a named table with a call back function to the query cache. @param thd The thread handle
@param table_key A pointer to the table name in the table cache
@param key_length The length of the table name
@param[out] engine_callback The pointer to the storage engine call back
function
@param[out] engine_data Storage engine specific data which could be
anything This method offers the storage engine, the possibility to store a reference
to a table name which is going to be used with query cache.
The method is called each time a statement is written to the cache and can
be used to verify if a specific statement is cachable. It also offers
the possibility to register a generic (but static) call back function which
is called each time a statement is matched against the query cache. @note If engine_data supplied with this function is different from
engine_data supplied with the callback function, and the callback returns
FALSE, a table invalidation on the current table will occur. @return Upon success the engine_callback will point to the storage engine
call back function, if any, and engine_data will point to any storage
engine data used in the specific implementation.
@retval TRUE Success
@retval FALSE The specified table or current statement should not be
cached
*/ virtual my_bool register_query_cache_table(THD *thd, char *table_key,
uint key_length,
qc_engine_callback
*engine_callback,
ulonglong *engine_data)
{
*engine_callback= ;
return TRUE;
} /*
@retval TRUE Primary key (if there is one) is clustered
key covering all fields
@retval FALSE otherwise
*/
virtual bool primary_key_is_clustered() { return FALSE; }
virtual int cmp_ref(const uchar *ref1, const uchar *ref2)
{
return memcmp(ref1, ref2, ref_length);
} /*
Condition pushdown to storage engines
*/ /**
Push condition down to the table handler. @param cond Condition to be pushed. The condition tree must not be
modified by the by the caller. @return
The 'remainder' condition that caller must use to filter out records.
NULL means the handler will not return rows that do not match the
passed condition. @note
The pushed conditions form a stack (from which one can remove the
last pushed condition using cond_pop).
The table handler filters out rows using (pushed_cond1 AND pushed_cond2
AND ... AND pushed_condN)
or less restrictive condition, depending on handler's capabilities. handler->ha_reset() call empties the condition stack.
Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
condition stack.
*/
virtual const COND *cond_push(const COND *cond) { return cond; };
/**
Pop the top condition from the condition stack of the handler instance. Pops the top if condition stack, if stack is not empty.
*/
virtual void cond_pop() { return; };
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes)
{ return COMPATIBLE_DATA_NO; } /**
use_hidden_primary_key() is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key
*/
virtual void use_hidden_primary_key();
virtual uint alter_table_flags(uint flags)
{
if (ht->alter_table_flags)
return ht->alter_table_flags(flags);
return ;
} protected:
/* Service methods for use by storage engines. */
void ha_statistic_increment(ulong SSV::*offset) const;
void **ha_data(THD *) const;
THD *ha_thd(void) const; /**
Acquire the instrumented table information from a table share.
@param share a table share
@return an instrumented table share, or NULL.
*/
PSI_table_share *ha_table_share_psi(const TABLE_SHARE *share) const; inline void psi_open()
{
DBUG_ASSERT(m_psi == NULL);
DBUG_ASSERT(table_share != NULL);
#ifdef HAVE_PSI_INTERFACE
if (PSI_server)
{
PSI_table_share *share_psi= ha_table_share_psi(table_share);
if (share_psi)
m_psi= PSI_server->open_table(share_psi, this);
}
#endif
} inline void psi_close()
{
#ifdef HAVE_PSI_INTERFACE
if (PSI_server && m_psi)
{
PSI_server->close_table(m_psi);
m_psi= NULL; /* instrumentation handle, invalid after close_table() */
}
#endif
DBUG_ASSERT(m_psi == NULL);
} /**
Default rename_table() and delete_table() rename/delete files with a
given name and extensions from bas_ext(). These methods can be overridden, but their default implementation
provide useful functionality.
*/
virtual int rename_table(const char *from, const char *to);
/**
Delete a table in the engine. Called for base as well as temporary
tables.
*/
virtual int delete_table(const char *name);
private:
/* Private helpers */
inline void mark_trx_read_write();
private:
/*
Low-level primitives for storage engines. These should be
overridden by the storage engine class. To call these methods, use
the corresponding 'ha_*' method above.
*/ virtual int open(const char *name, int mode, uint test_if_locked)=;
virtual int index_init(uint idx, bool sorted) { active_index= idx; return ; }
virtual int index_end() { active_index= MAX_KEY; return ; }
/**
rnd_init() can be called two times without rnd_end() in between
(it only makes sense if scan=1).
then the second call should prepare for the new table scan (e.g
if rnd_init allocates the cursor, second call should position it
to the start of the table, no need to deallocate and allocate it again
*/
virtual int rnd_init(bool scan)= ;
virtual int rnd_end() { return ; }
virtual int write_row(uchar *buf __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
} virtual int update_row(const uchar *old_data __attribute__((unused)),
uchar *new_data __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
} virtual int delete_row(const uchar *buf __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
}
/**
Reset state of file to after 'open'.
This function is called after every statement for all tables used
by that statement.
*/
virtual int reset() { return ; }
virtual Table_flags table_flags(void) const= ;
/**
Is not invoked for non-transactional temporary tables. Tells the storage engine that we intend to read or write data
from the table. This call is prefixed with a call to handler::store_lock()
and is invoked only for those handler instances that stored the lock. Calls to rnd_init/index_init are prefixed with this call. When table
IO is complete, we call external_lock(F_UNLCK).
A storage engine writer should expect that each call to
::external_lock(F_[RD|WR]LOCK is followed by a call to
::external_lock(F_UNLCK). If it is not, it is a bug in MySQL. The name and signature originate from the first implementation
in MyISAM, which would call fcntl to set/clear an advisory
lock on the data file in this method. @param lock_type F_RDLCK, F_WRLCK, F_UNLCK @return non-0 in case of failure, 0 in case of success.
When lock_type is F_UNLCK, the return value is ignored.
*/
virtual int external_lock(THD *thd __attribute__((unused)),
int lock_type __attribute__((unused)))
{
return ;
}
virtual void release_auto_increment() { return; };
/** admin commands - called from mysql_admin_table */
virtual int check_for_upgrade(HA_CHECK_OPT *check_opt)
{ return ; }
virtual int check(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; } /**
In this method check_opt can be modified
to specify CHECK option to use to call check()
upon the table.
*/
virtual int repair(THD* thd, HA_CHECK_OPT* check_opt)
{
DBUG_ASSERT(!(ha_table_flags() & HA_CAN_REPAIR));
return HA_ADMIN_NOT_IMPLEMENTED;
}
virtual void start_bulk_insert(ha_rows rows) {}
virtual int end_bulk_insert() { return ; }
virtual int index_read(uchar * buf, const uchar * key, uint key_len,
enum ha_rkey_function find_flag)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_read_last(uchar * buf, const uchar * key, uint key_len)
{ return (my_errno= HA_ERR_WRONG_COMMAND); }
/**
This method is similar to update_row, however the handler doesn't need
to execute the updates at this point in time. The handler can be certain
that another call to bulk_update_row will occur OR a call to
exec_bulk_update before the set of updates in this query is concluded. @param old_data Old record
@param new_data New record
@param dup_key_found Number of duplicate keys found @retval 0 Bulk delete used by handler
@retval 1 Bulk delete not used, normal operation used
*/
virtual int bulk_update_row(const uchar *old_data, uchar *new_data,
uint *dup_key_found)
{
DBUG_ASSERT(FALSE);
return HA_ERR_WRONG_COMMAND;
}
/**
This is called to delete all rows in a table
If the handler don't support this, then this function will
return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one
by one.
*/
virtual int delete_all_rows()
{ return (my_errno=HA_ERR_WRONG_COMMAND); }
/**
Quickly remove all rows from a table. @remark This method is responsible for implementing MySQL's TRUNCATE
TABLE statement, which is a DDL operation. As such, a engine
can bypass certain integrity checks and in some cases avoid
fine-grained locking (e.g. row locks) which would normally be
required for a DELETE statement. @remark Typically, truncate is not used if it can result in integrity
violation. For example, truncate is not used when a foreign
key references the table, but it might be used if foreign key
checks are disabled. @remark Engine is responsible for resetting the auto-increment counter. @remark The table is locked in exclusive mode.
*/
virtual int truncate()
{ return HA_ERR_WRONG_COMMAND; }
/**
Reset the auto-increment counter to the given value, i.e. the next row
inserted will get the given value. HA_ERR_WRONG_COMMAND is returned by
storage engines that don't support this operation.
*/
virtual int reset_auto_increment(ulonglong value)
{ return HA_ERR_WRONG_COMMAND; }
virtual int optimize(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
virtual bool check_and_repair(THD *thd) { return TRUE; }
virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int discard_or_import_tablespace(my_bool discard)
{ return (my_errno=HA_ERR_WRONG_COMMAND); }
virtual void prepare_for_alter() { return; }
virtual void drop_table(const char *name);
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=; virtual int create_handler_files(const char *name, const char *old_name,
int action_flag, HA_CREATE_INFO *info)
{ return FALSE; } virtual int change_partitions(HA_CREATE_INFO *create_info,
const char *path,
ulonglong * const copied,
ulonglong * const deleted,
const uchar *pack_frm_data,
size_t pack_frm_len)
{ return HA_ERR_WRONG_COMMAND; }
virtual int drop_partitions(const char *path)
{ return HA_ERR_WRONG_COMMAND; }
virtual int rename_partitions(const char *path)
{ return HA_ERR_WRONG_COMMAND; }
};
05-11 14:01