/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- * vim:expandtab:shiftwidth=2:tabstop=2:smarttab: * * Copyright (C) 2008 Sun Microsystems * * 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-1301 USA */ #ifndef DRIZZLED_HANDLER_H #define DRIZZLED_HANDLER_H #include /* Definitions for parameters to do with handler-routines */ #include #include #include #include #include /* Bits to show what an alter table will do */ #include #define HA_MAX_ALTER_FLAGS 40 typedef Bitmap HA_ALTER_FLAGS; typedef bool (*qc_engine_callback)(Session *session, char *table_key, uint32_t key_length, uint64_t *engine_data); struct handlerton; /* The handler for a table type. Will be included in the Table structure */ class Table; class TableList; typedef struct st_table_share TABLE_SHARE; struct st_foreign_key_info; typedef struct st_foreign_key_info FOREIGN_KEY_INFO; typedef bool (stat_print_fn)(Session *session, const char *type, uint32_t type_len, const char *file, uint32_t file_len, const char *status, uint32_t status_len); enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX }; extern st_plugin_int *hton2plugin[MAX_HA]; /* handlerton is a singleton structure - one instance per storage engine - to provide access to storage engine functionality that works on the "global" level (unlike handler class that works on a per-table basis) usually handlerton instance is defined statically in ha_xxx.cc as static handlerton { ... } xxx_hton; savepoint_*, prepare, recover, and *_by_xid pointers can be 0. */ struct handlerton { /* Historical marker for if the engine is available of not */ SHOW_COMP_OPTION state; /* Historical number used for frm file to determine the correct storage engine. This is going away and new engines will just use "name" for this. */ enum legacy_db_type db_type; /* each storage engine has it's own memory area (actually a pointer) in the session, for storing per-connection information. It is accessed as session->ha_data[xxx_hton.slot] slot number is initialized by MySQL after xxx_init() is called. */ uint32_t slot; /* to store per-savepoint data storage engine is provided with an area of a requested size (0 is ok here). savepoint_offset must be initialized statically to the size of the needed memory to store per-savepoint information. After xxx_init it is changed to be an offset to savepoint storage area and need not be used by storage engine. see binlog_hton and binlog_savepoint_set/rollback for an example. */ uint32_t savepoint_offset; /* handlerton methods: close_connection is only called if session->ha_data[xxx_hton.slot] is non-zero, so even if you don't need this storage area - set it to something, so that MySQL would know this storage engine was accessed in this connection */ int (*close_connection)(handlerton *hton, Session *session); /* sv points to an uninitialized storage area of requested size (see savepoint_offset description) */ int (*savepoint_set)(handlerton *hton, Session *session, void *sv); /* sv points to a storage area, that was earlier passed to the savepoint_set call */ int (*savepoint_rollback)(handlerton *hton, Session *session, void *sv); int (*savepoint_release)(handlerton *hton, Session *session, void *sv); /* 'all' is true if it's a real commit, that makes persistent changes 'all' is false if it's not in fact a commit but an end of the statement that is part of the transaction. NOTE 'all' is also false in auto-commit mode where 'end of statement' and 'real commit' mean the same event. */ int (*commit)(handlerton *hton, Session *session, bool all); int (*rollback)(handlerton *hton, Session *session, bool all); int (*prepare)(handlerton *hton, Session *session, bool all); int (*recover)(handlerton *hton, XID *xid_list, uint32_t len); int (*commit_by_xid)(handlerton *hton, XID *xid); int (*rollback_by_xid)(handlerton *hton, XID *xid); void *(*create_cursor_read_view)(handlerton *hton, Session *session); void (*set_cursor_read_view)(handlerton *hton, Session *session, void *read_view); void (*close_cursor_read_view)(handlerton *hton, Session *session, void *read_view); handler *(*create)(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root); void (*drop_database)(handlerton *hton, char* path); int (*start_consistent_snapshot)(handlerton *hton, Session *session); bool (*flush_logs)(handlerton *hton); bool (*show_status)(handlerton *hton, Session *session, stat_print_fn *print, enum ha_stat_type stat); int (*fill_files_table)(handlerton *hton, Session *session, TableList *tables, class Item *cond); uint32_t flags; /* global handler flags */ int (*release_temporary_latches)(handlerton *hton, Session *session); int (*discover)(handlerton *hton, Session* session, const char *db, const char *name, unsigned char **frmblob, size_t *frmlen); int (*table_exists_in_engine)(handlerton *hton, Session* session, const char *db, const char *name); uint32_t license; /* Flag for Engine License */ void *data; /* Location for engines to keep personal structures */ }; class Ha_trx_info; struct Session_TRANS { /* true is not all entries in the ht[] support 2pc */ bool no_2pc; /* storage engines that registered in this transaction */ Ha_trx_info *ha_list; /* The purpose of this flag is to keep track of non-transactional tables that were modified in scope of: - transaction, when the variable is a member of Session::transaction.all - top-level statement or sub-statement, when the variable is a member of Session::transaction.stmt This member has the following life cycle: * stmt.modified_non_trans_table is used to keep track of modified non-transactional tables of top-level statements. At the end of the previous statement and at the beginning of the session, it is reset to false. If such functions as mysql_insert, mysql_update, mysql_delete etc modify a non-transactional table, they set this flag to true. At the end of the statement, the value of stmt.modified_non_trans_table is merged with all.modified_non_trans_table and gets reset. * all.modified_non_trans_table is reset at the end of transaction * Since we do not have a dedicated context for execution of a sub-statement, to keep track of non-transactional changes in a sub-statement, we re-use stmt.modified_non_trans_table. At entrance into a sub-statement, a copy of the value of stmt.modified_non_trans_table (containing the changes of the outer statement) is saved on stack. Then stmt.modified_non_trans_table is reset to false and the substatement is executed. Then the new value is merged with the saved value. */ bool modified_non_trans_table; void reset() { no_2pc= false; modified_non_trans_table= false; } }; /** Either statement transaction or normal transaction - related thread-specific storage engine data. If a storage engine participates in a statement/transaction, an instance of this class is present in session->transaction.{stmt|all}.ha_list. The addition to {stmt|all}.ha_list is made by trans_register_ha(). When it's time to commit or rollback, each element of ha_list is used to access storage engine's prepare()/commit()/rollback() methods, and also to evaluate if a full two phase commit is necessary. @sa General description of transaction handling in handler.cc. */ class Ha_trx_info { public: /** Register this storage engine in the given transaction context. */ void register_ha(Session_TRANS *trans, handlerton *ht_arg) { assert(m_flags == 0); assert(m_ht == NULL); assert(m_next == NULL); m_ht= ht_arg; m_flags= (int) TRX_READ_ONLY; /* Assume read-only at start. */ m_next= trans->ha_list; trans->ha_list= this; } /** Clear, prepare for reuse. */ void reset() { m_next= NULL; m_ht= NULL; m_flags= 0; } Ha_trx_info() { reset(); } void set_trx_read_write() { assert(is_started()); m_flags|= (int) TRX_READ_WRITE; } bool is_trx_read_write() const { assert(is_started()); return m_flags & (int) TRX_READ_WRITE; } bool is_started() const { return m_ht != NULL; } /** Mark this transaction read-write if the argument is read-write. */ void coalesce_trx_with(const Ha_trx_info *stmt_trx) { /* Must be called only after the transaction has been started. Can be called many times, e.g. when we have many read-write statements in a transaction. */ assert(is_started()); if (stmt_trx->is_trx_read_write()) set_trx_read_write(); } Ha_trx_info *next() const { assert(is_started()); return m_next; } handlerton *ht() const { assert(is_started()); return m_ht; } private: enum { TRX_READ_ONLY= 0, TRX_READ_WRITE= 1 }; /** Auxiliary, used for ha_list management */ Ha_trx_info *m_next; /** Although a given Ha_trx_info instance is currently always used for the same storage engine, 'ht' is not-NULL only when the corresponding storage is a part of a transaction. */ handlerton *m_ht; /** Transaction flags related to this engine. Not-null only if this instance is a part of transaction. May assume a combination of enum values above. */ unsigned char m_flags; }; enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, ISO_REPEATABLE_READ, ISO_SERIALIZABLE}; typedef struct { uint64_t data_file_length; uint64_t max_data_file_length; uint64_t index_file_length; uint64_t delete_length; ha_rows records; uint32_t mean_rec_length; time_t create_time; time_t check_time; time_t update_time; uint64_t check_sum; } PARTITION_INFO; class Item; struct st_table_log_memory_entry; typedef struct st_ha_create_information { const CHARSET_INFO *table_charset, *default_table_charset; LEX_STRING connect_string; LEX_STRING comment; const char *data_file_name, *index_file_name; const char *alias; uint64_t max_rows,min_rows; uint64_t auto_increment_value; uint32_t table_options; uint32_t avg_row_length; uint32_t used_fields; uint32_t key_block_size; uint32_t block_size; handlerton *db_type; enum row_type row_type; uint32_t null_bits; /* NULL bits at start of record */ uint32_t options; /* OR of HA_CREATE_ options */ uint32_t extra_size; /* length of extra data segment */ bool table_existed; /* 1 in create if table existed */ bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ enum ha_choice page_checksum; /* If we have page_checksums */ } HA_CREATE_INFO; typedef struct st_ha_alter_information { KEY *key_info_buffer; uint32_t key_count; uint32_t index_drop_count; uint32_t *index_drop_buffer; uint32_t index_add_count; uint32_t *index_add_buffer; void *data; } HA_ALTER_INFO; typedef struct st_key_create_information { enum ha_key_alg algorithm; uint32_t block_size; LEX_STRING parser_name; LEX_STRING comment; } KEY_CREATE_INFO; /* Class for maintaining hooks used inside operations on tables such as: create table functions, delete table functions, and alter table functions. Class is using the Template Method pattern to separate the public usage interface from the private inheritance interface. This imposes no overhead, since the public non-virtual function is small enough to be inlined. The hooks are usually used for functions that does several things, e.g., create_table_from_items(), which both create a table and lock it. */ class TABLEOP_HOOKS { public: TABLEOP_HOOKS() {} virtual ~TABLEOP_HOOKS() {} inline void prelock(Table **tables, uint32_t count) { do_prelock(tables, count); } inline int postlock(Table **tables, uint32_t count) { return do_postlock(tables, count); } private: /* Function primitive that is called prior to locking tables */ virtual void do_prelock(Table **tables __attribute__((unused)), uint32_t count __attribute__((unused))) { /* Default is to do nothing */ } /** Primitive called after tables are locked. If an error is returned, the tables will be unlocked and error handling start. @return Error code or zero. */ virtual int do_postlock(Table **tables __attribute__((unused)), uint32_t count __attribute__((unused))) { return 0; /* Default is to do nothing */ } }; typedef struct st_savepoint SAVEPOINT; extern uint32_t savepoint_alloc_size; extern KEY_CREATE_INFO default_key_create_info; /* Forward declaration for condition pushdown to storage engine */ typedef class Item COND; typedef struct st_ha_check_opt { st_ha_check_opt() {} /* Remove gcc warning */ uint32_t sort_buffer_size; uint32_t flags; /* isam layer flags (e.g. for myisamchk) */ uint32_t sql_flags; /* sql layer flags - for something myisamchk cannot do */ KEY_CACHE *key_cache; /* new key cache when changing key cache */ void init(); } HA_CHECK_OPT; /* This is a buffer area that the handler can use to store rows. 'end_of_used_area' should be kept updated after calls to read-functions so that other parts of the code can use the remaining area (until next read calls is issued). */ typedef struct st_handler_buffer { unsigned char *buffer; /* Buffer one can start using */ unsigned char *buffer_end; /* End of buffer */ unsigned char *end_of_used_area; /* End of area that was used by handler */ } HANDLER_BUFFER; typedef struct system_status_var SSV; typedef void *range_seq_t; typedef struct st_range_seq_if { /* Initialize the traversal of range sequence SYNOPSIS init() init_params The seq_init_param parameter n_ranges The number of ranges obtained flags A combination of HA_MRR_SINGLE_POINT, HA_MRR_FIXED_KEY RETURN An opaque value to be used as RANGE_SEQ_IF::next() parameter */ range_seq_t (*init)(void *init_params, uint32_t n_ranges, uint32_t flags); /* Get the next range in the range sequence SYNOPSIS next() seq The value returned by RANGE_SEQ_IF::init() range OUT Information about the next range RETURN 0 - Ok, the range structure filled with info about the next range 1 - No more ranges */ uint32_t (*next) (range_seq_t seq, KEY_MULTI_RANGE *range); } RANGE_SEQ_IF; uint16_t &mrr_persistent_flag_storage(range_seq_t seq, uint32_t idx); char* &mrr_get_ptr_by_idx(range_seq_t seq, uint32_t idx); class COST_VECT { public: double io_count; /* number of I/O */ double avg_io_cost; /* cost of an average I/O oper. */ double cpu_cost; /* cost of operations in CPU */ double mem_cost; /* cost of used memory */ double import_cost; /* cost of remote operations */ enum { IO_COEFF=1 }; enum { CPU_COEFF=1 }; enum { MEM_COEFF=1 }; enum { IMPORT_COEFF=1 }; COST_VECT() {} // keep gcc happy double total_cost() { return IO_COEFF*io_count*avg_io_cost + CPU_COEFF * cpu_cost + MEM_COEFF*mem_cost + IMPORT_COEFF*import_cost; } void zero() { avg_io_cost= 1.0; io_count= cpu_cost= mem_cost= import_cost= 0.0; } void multiply(double m) { io_count *= m; cpu_cost *= m; import_cost *= m; /* Don't multiply mem_cost */ } void add(const COST_VECT* cost) { double io_count_sum= io_count + cost->io_count; add_io(cost->io_count, cost->avg_io_cost); io_count= io_count_sum; cpu_cost += cost->cpu_cost; } void add_io(double add_io_cnt, double add_avg_cost) { double io_count_sum= io_count + add_io_cnt; avg_io_cost= (io_count * avg_io_cost + add_io_cnt * add_avg_cost) / io_count_sum; io_count= io_count_sum; } }; void get_sweep_read_cost(Table *table, ha_rows nrows, bool interrupted, COST_VECT *cost); class ha_statistics { public: uint64_t data_file_length; /* Length off data file */ uint64_t max_data_file_length; /* Length off data file */ uint64_t index_file_length; uint64_t max_index_file_length; uint64_t delete_length; /* Free bytes */ uint64_t auto_increment_value; /* The number of records in the table. 0 - means the table has exactly 0 rows other - if (table_flags() & HA_STATS_RECORDS_IS_EXACT) the value is the exact number of records in the table else it is an estimate */ ha_rows records; ha_rows deleted; /* Deleted records */ uint32_t mean_rec_length; /* physical reclength */ time_t create_time; /* When table was created */ time_t check_time; time_t update_time; uint32_t block_size; /* index block size */ ha_statistics(): data_file_length(0), max_data_file_length(0), index_file_length(0), delete_length(0), auto_increment_value(0), records(0), deleted(0), mean_rec_length(0), create_time(0), check_time(0), update_time(0), block_size(0) {} }; uint32_t calculate_key_len(Table *, uint, const unsigned char *, key_part_map); /* bitmap with first N+1 bits set (keypart_map for a key prefix of [0..N] keyparts) */ #define make_keypart_map(N) (((key_part_map)2 << (N)) - 1) /* bitmap with first N bits set (keypart_map for a key prefix of [0..N-1] keyparts) */ #define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1) /** 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 Functions in this class accept and return table columns data. Two data representation formats are used: 1. TableRecordFormat - Used to pass [partial] table records to/from storage engine 2. KeyTupleFormat - used to pass index search tuples (aka "keys") to storage engine. See opt_range.cc for description of this format. TableRecordFormat ================= [Warning: this description is work in progress and may be incomplete] The table record is stored in a fixed-size buffer: record: null_bytes, column1_data, column2_data, ... The offsets of the parts of the buffer are also fixed: every column has an offset to its column{i}_data, and if it is nullable it also has its own bit in null_bytes. The record buffer only includes data about columns that are marked in the relevant column set (table->read_set and/or table->write_set, depending on the situation). It could be that it is required that null bits of non-present columns are set to 1 VARIOUS EXCEPTIONS AND SPECIAL CASES f the table has no nullable columns, then null_bytes is still present, its length is one byte which must be set to 0xFF at all times. If the table has columns of type BIT, then certain bits from those columns may be stored in null_bytes as well. Grep around for Field_bit for details. For blob columns (see Field_blob), the record buffer stores length of the data, following by memory pointer to the blob data. The pointer is owned by the storage engine and is valid until the next operation. If a blob column has NULL value, then its length and blob data pointer must be set to 0. */ class handler :public Sql_alloc { public: typedef uint64_t Table_flags; protected: struct st_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 */ unsigned char *ref; /* Pointer to current row */ unsigned char *dup_ref; /* Pointer to duplicate row */ ha_statistics stats; /** MultiRangeRead-related members: */ range_seq_t mrr_iter; /* Interator to traverse the range sequence */ RANGE_SEQ_IF mrr_funcs; /* Range sequence traversal functions */ HANDLER_BUFFER *multi_range_buffer; /* MRR buffer info */ uint32_t ranges_in_seq; /* Total number of ranges in the traversed sequence */ /* true <=> source MRR ranges and the output are ordered */ bool mrr_is_output_sorted; /** true <=> we're currently traversing a range in mrr_cur_range. */ bool mrr_have_range; /** Current range (the one we're now returning rows from) */ KEY_MULTI_RANGE mrr_cur_range; /** 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; /* true <=> the engine guarantees that returned records are within the range being scanned. */ bool in_range_check_pushed_down; uint32_t errkey; /* Last dup key */ uint32_t key_used_on_scan; uint32_t active_index; /** Length of ref (1-8 or the clustered key length) */ uint32_t ref_length; enum {NONE=0, INDEX, RND} inited; bool locked; bool implicit_emptied; /* Can be !=0 only if HEAP */ const Item *pushed_cond; Item *pushed_idx_cond; uint32_t pushed_idx_cond_keyno; /* The index which the above condition is for */ /** 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(). */ uint64_t 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 Session::first_successful_insert_id_in_cur_stmt. */ uint64_t 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; handler(handlerton *ht_arg, TABLE_SHARE *share_arg) :table_share(share_arg), table(0), estimation_rows_to_insert(0), ht(ht_arg), ref(0), in_range_check_pushed_down(false), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), inited(NONE), locked(false), implicit_emptied(0), pushed_cond(0), pushed_idx_cond(NULL), pushed_idx_cond_keyno(MAX_KEY), next_insert_id(0), insert_id_for_cur_row(0) {} virtual ~handler(void) { assert(locked == false); /* TODO: assert(inited == NONE); */ } virtual handler *clone(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(uint32_t idx, bool sorted) { int result; assert(inited==NONE); if (!(result= index_init(idx, sorted))) inited=INDEX; end_range= NULL; return(result); } int ha_index_end() { assert(inited==INDEX); inited=NONE; end_range= NULL; return(index_end()); } int ha_rnd_init(bool scan) { int result; assert(inited==NONE || (inited==RND && scan)); inited= (result= rnd_init(scan)) ? NONE: RND; return(result); } int ha_rnd_end() { assert(inited==RND); inited=NONE; 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() : 0; } 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(Session *session, int lock_type); int ha_write_row(unsigned char * buf); int ha_update_row(const unsigned char * old_data, unsigned char * new_data); int ha_delete_row(const unsigned char * buf); void ha_release_auto_increment(); int ha_check_for_upgrade(HA_CHECK_OPT *check_opt); /** to be actually called to get 'check()' functionality*/ int ha_check(Session *session, HA_CHECK_OPT *check_opt); int ha_repair(Session* session, 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= 0; return end_bulk_insert(); } int ha_bulk_update_row(const unsigned char *old_data, unsigned char *new_data, uint32_t *dup_key_found); int ha_delete_all_rows(); int ha_reset_auto_increment(uint64_t value); int ha_optimize(Session* session, HA_CHECK_OPT* check_opt); int ha_analyze(Session* session, HA_CHECK_OPT* check_opt); bool ha_check_and_repair(Session *session); int ha_disable_indexes(uint32_t mode); int ha_enable_indexes(uint32_t mode); int ha_discard_or_import_tablespace(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); void adjust_next_insert_id_after_explicit_value(uint64_t nr); int update_auto_increment(); void print_keydup_error(uint32_t key_nr, const char *msg); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint32_t get_dup_key(int error); virtual void change_table_ptr(Table *table_arg, TABLE_SHARE *share) { table= table_arg; table_share= share; } /* Estimates calculation */ virtual double scan_time(void) { return uint64_t2double(stats.data_file_length) / IO_SIZE + 2; } virtual double read_time(uint32_t index __attribute__((unused)), uint32_t ranges, ha_rows rows) { return rows2double(ranges+rows); } virtual double index_only_read_time(uint32_t keynr, double records); virtual ha_rows multi_range_read_info_const(uint32_t keyno, RANGE_SEQ_IF *seq, void *seq_init_param, uint32_t n_ranges, uint32_t *bufsz, uint32_t *flags, COST_VECT *cost); virtual int multi_range_read_info(uint32_t keyno, uint32_t n_ranges, uint32_t keys, uint32_t *bufsz, uint32_t *flags, COST_VECT *cost); virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, uint32_t n_ranges, uint32_t mode, HANDLER_BUFFER *buf); virtual int multi_range_read_next(char **range_info); virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } bool has_transactions() { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; } virtual uint32_t extra_rec_buf_length() const { return 0; } /** 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, uint32_t 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(uint32_t key_number __attribute__((unused))) { assert(0); 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(); uint32_t get_index(void) const { return active_index; } virtual int close(void)=0; /** @retval 0 Bulk update used by handler @retval 1 Bulk update not used, normal operation used */ virtual bool start_bulk_update() { return 1; } /** @retval 0 Bulk delete used by handler @retval 1 Bulk delete not used, normal operation used */ virtual bool start_bulk_delete() { return 1; } /** 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(uint32_t *dup_key_found __attribute__((unused))) { 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() { 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(unsigned char * buf, const unsigned char * key, key_part_map keypart_map, enum ha_rkey_function find_flag) { uint32_t 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(unsigned char * buf, uint32_t index, const unsigned char * key, key_part_map keypart_map, enum ha_rkey_function find_flag); virtual int index_next(unsigned char * buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int index_prev(unsigned char * buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int index_first(unsigned char * buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int index_last(unsigned char * buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int index_next_same(unsigned char *buf __attribute__((unused)), const unsigned char *key __attribute__((unused)), uint32_t keylen __attribute__((unused))); /** @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(unsigned char * buf, const unsigned char * key, key_part_map keypart_map) { uint32_t key_len= calculate_key_len(table, active_index, key, keypart_map); return index_read_last(buf, key, key_len); } 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); int compare_key2(key_range *range); virtual int rnd_next(unsigned char *buf __attribute__((unused)))=0; virtual int rnd_pos(unsigned char * buf __attribute__((unused)), unsigned char *pos __attribute__((unused)))=0; /** One has to use this method when to find random position by record as the plain position() call doesn't work for some handlers for random position. */ virtual int rnd_pos_by_record(unsigned char *record); virtual int read_first_row(unsigned char *buf, uint32_t primary_key); /** The following function is only needed for tables that may be temporary tables during joins. */ virtual int restart_rnd_next(unsigned char *buf __attribute__((unused)), unsigned char *pos __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_same(unsigned char *buf __attribute__((unused)), uint32_t inx __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual ha_rows records_in_range(uint32_t inx __attribute__((unused)), key_range *min_key __attribute__((unused)), key_range *max_key __attribute__((unused))) { return (ha_rows) 10; } virtual void position(const unsigned char *record)=0; virtual int info(uint)=0; // see my_base.h for full description virtual uint32_t calculate_key_hash_value(Field **field_array __attribute__((unused))) { assert(0); return 0; } virtual int extra(enum ha_extra_function operation __attribute__((unused))) { return 0; } virtual int extra_opt(enum ha_extra_function operation, uint32_t cache_size __attribute__((unused))) { 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 0; } /** 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(void) {} virtual int start_stmt(Session *session __attribute__((unused)), thr_lock_type lock_type __attribute__((unused))) {return 0;} virtual void get_auto_increment(uint64_t offset, uint64_t increment, uint64_t nb_desired_values, uint64_t *first_value, uint64_t *nb_reserved_values); void set_next_insert_id(uint64_t id) { next_insert_id= id; } void restore_auto_increment(uint64_t 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 > 0) ? prev_insert_id : insert_id_for_cur_row; } virtual void update_create_info(HA_CREATE_INFO *create_info __attribute__((unused))) {} int check_old_types(void); virtual int assign_to_keycache(Session* session __attribute__((unused)), HA_CHECK_OPT* check_opt __attribute__((unused))) { return HA_ADMIN_NOT_IMPLEMENTED; } /* end of the list of admin commands */ virtual int indexes_are_disabled(void) {return 0;} virtual char *update_table_comment(const char * comment) { return (char*) comment;} virtual void append_create_info(String *packet __attribute__((unused))) {} /** 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(uint32_t index __attribute__((unused))) { return false; } virtual char* get_foreign_key_create_info(void) { return(NULL);} /* gets foreign key create string from InnoDB */ /** used in ALTER Table; 1 if changing storage engine is allowed */ virtual bool can_switch_engines(void) { return 1; } /** used in REPLACE; is > 0 if table is referred by a FOREIGN KEY */ virtual int get_foreign_key_list(Session *session __attribute__((unused)), List *f_key_list __attribute__((unused))) { return 0; } virtual uint32_t referenced_by_foreign_key() { return 0;} virtual void init_table_handle_for_HANDLER() { return; } /* prepare InnoDB for HANDLER */ virtual void free_foreign_key_create_info(char* str __attribute__((unused))) {} /** The following can be called without an open handler */ virtual const char *table_type() const =0; /** 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 =0; virtual int get_default_no_partitions(HA_CREATE_INFO *info __attribute__((unused))) { return 1;} virtual bool get_no_parts(const char *name __attribute__((unused)), uint32_t *no_parts) { *no_parts= 0; return 0; } virtual uint32_t index_flags(uint32_t idx, uint32_t part, bool all_parts) const =0; virtual int add_index(Table *table_arg __attribute__((unused)), KEY *key_info __attribute__((unused)), uint32_t num_of_keys __attribute__((unused))) { return (HA_ERR_WRONG_COMMAND); } virtual int prepare_drop_index(Table *table_arg __attribute__((unused)), uint32_t *key_num __attribute__((unused)), uint32_t num_of_keys __attribute__((unused))) { return (HA_ERR_WRONG_COMMAND); } virtual int final_drop_index(Table *table_arg __attribute__((unused))) { return (HA_ERR_WRONG_COMMAND); } uint32_t max_record_length() const { return cmin((unsigned int)HA_MAX_REC_LENGTH, max_supported_record_length()); } uint32_t max_keys() const { return cmin((unsigned int)MAX_KEY, max_supported_keys()); } uint32_t max_key_parts() const { return cmin((unsigned int)MAX_REF_PARTS, max_supported_key_parts()); } uint32_t max_key_length() const { return cmin((unsigned int)MAX_KEY_LENGTH, max_supported_key_length()); } uint32_t max_key_part_length(void) const { return cmin((unsigned int)MAX_KEY_LENGTH, max_supported_key_part_length()); } virtual uint32_t max_supported_record_length(void) const { return HA_MAX_REC_LENGTH; } virtual uint32_t max_supported_keys(void) const { return 0; } virtual uint32_t max_supported_key_parts(void) const { return MAX_REF_PARTS; } virtual uint32_t max_supported_key_length(void) const { return MAX_KEY_LENGTH; } virtual uint32_t max_supported_key_part_length(void) const { return 255; } virtual uint32_t min_record_length(uint32_t options __attribute__((unused))) const { return 1; } virtual bool low_byte_first(void) const { return 1; } virtual uint32_t checksum(void) const { return 0; } virtual bool is_crashed(void) const { return 0; } virtual bool auto_repair(void) const { return 0; } #define CHF_CREATE_FLAG 0 #define CHF_DELETE_FLAG 1 #define CHF_RENAME_FLAG 2 /** @note lock_count() can return > 1 if the table is MERGE or partitioned. */ virtual uint32_t lock_count(void) const { return 1; } /** 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(Session *session, THR_LOCK_DATA **to, enum thr_lock_type lock_type)=0; /** Type of table for caching query */ virtual uint8_t table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } /** @brief Register a named table with a call back function to the query cache. @param session 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 bool register_query_cache_table(Session *session __attribute__((unused)), char *table_key __attribute__((unused)), uint32_t key_length __attribute__((unused)), qc_engine_callback *engine_callback, uint64_t *engine_data __attribute__((unused))) { *engine_callback= 0; 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 unsigned char *ref1, const unsigned char *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(void) { return; } virtual Item *idx_cond_push(uint32_t keyno __attribute__((unused)), Item* idx_cond __attribute__((unused))) { return idx_cond; } /* Part of old fast alter table, to be depricated */ virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info __attribute__((unused)), uint32_t table_changes __attribute__((unused))) { return COMPATIBLE_DATA_NO; } /* On-line ALTER Table interface */ /** Check if a storage engine supports a particular alter table on-line @param altered_table A temporary table show what table is to change to @param create_info Information from the parsing phase about new table properties. @param alter_flags Bitmask that shows what will be changed @param table_changes Shows if table layout has changed (for backwards compatibility with check_if_incompatible_data @retval HA_ALTER_ERROR Unexpected error @retval HA_ALTER_SUPPORTED_WAIT_LOCK Supported, but requires DDL lock @retval HA_ALTER_SUPPORTED_NO_LOCK Supported @retval HA_ALTER_NOT_SUPPORTED Not supported @note The default implementation is implemented to support fast alter table (storage engines that support some changes by just changing the frm file) without any change in the handler implementation. */ virtual int check_if_supported_alter(Table *altered_table __attribute__((unused)), HA_CREATE_INFO *create_info, HA_ALTER_FLAGS *alter_flags __attribute__((unused)), uint32_t table_changes) { if (this->check_if_incompatible_data(create_info, table_changes) == COMPATIBLE_DATA_NO) return(HA_ALTER_NOT_SUPPORTED); else if ((*alter_flags & HA_ALTER_STORED_VCOL).is_set()) return(HA_ALTER_NOT_SUPPORTED); else return(HA_ALTER_SUPPORTED_WAIT_LOCK); } /** Tell storage engine to prepare for the on-line alter table (pre-alter) @param session The thread handle @param altered_table A temporary table show what table is to change to @param alter_info Storage place for data used during phase1 and phase2 @param alter_flags Bitmask that shows what will be changed @retval 0 OK @retval error error code passed from storage engine */ virtual int alter_table_phase1(Session *session __attribute__((unused)), Table *altered_table __attribute__((unused)), HA_CREATE_INFO *create_info __attribute__((unused)), HA_ALTER_INFO *alter_info __attribute__((unused)), HA_ALTER_FLAGS *alter_flags __attribute__((unused))) { return HA_ERR_UNSUPPORTED; } /** Tell storage engine to perform the on-line alter table (alter) @param session The thread handle @param altered_table A temporary table show what table is to change to @param alter_info Storage place for data used during phase1 and phase2 @param alter_flags Bitmask that shows what will be changed @retval 0 OK @retval error error code passed from storage engine @note If check_if_supported_alter returns HA_ALTER_SUPPORTED_WAIT_LOCK this call is to be wrapped with a DDL lock. This is currently NOT supported. */ virtual int alter_table_phase2(Session *session __attribute__((unused)), Table *altered_table __attribute__((unused)), HA_CREATE_INFO *create_info __attribute__((unused)), HA_ALTER_INFO *alter_info __attribute__((unused)), HA_ALTER_FLAGS *alter_flags __attribute__((unused))) { return HA_ERR_UNSUPPORTED; } /** Tell storage engine that changed frm file is now on disk and table has been re-opened (post-alter) @param session The thread handle @param table The altered table, re-opened */ virtual int alter_table_phase3(Session *session __attribute__((unused)), Table *table __attribute__((unused))) { return HA_ERR_UNSUPPORTED; } /** 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(); /** Lock table. @param session Thread handle @param lock_type HA_LOCK_IN_SHARE_MODE (F_RDLCK) HA_LOCK_IN_EXCLUSIVE_MODE (F_WRLCK) @param lock_timeout -1 default timeout 0 no wait >0 wait timeout in milliseconds. @note lock_timeout >0 is not used by MySQL currently. If the storage engine does not support NOWAIT (lock_timeout == 0) it should return an error. But if it does not support WAIT X (lock_timeout >0) it should treat it as lock_timeout == -1 and wait a default (or even hard-coded) timeout. @retval HA_ERR_WRONG_COMMAND Storage engine does not support lock_table() @retval HA_ERR_UNSUPPORTED Storage engine does not support NOWAIT @retval HA_ERR_LOCK_WAIT_TIMEOUT Lock request timed out or lock conflict with NOWAIT option @retval HA_ERR_LOCK_DEADLOCK Deadlock detected */ virtual int lock_table(Session *session __attribute__((unused)), int lock_type __attribute__((unused)), int lock_timeout __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } /* This procedure defines if the storage engine supports virtual columns. Default false means "not supported". */ virtual bool check_if_supported_virtual_columns(void) { return false; } protected: /* Service methods for use by storage engines. */ void ha_statistic_increment(ulong SSV::*offset) const; void **ha_data(Session *) const; Session *ha_session(void) const; /** 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, uint32_t test_if_locked)=0; virtual int index_init(uint32_t idx, bool sorted __attribute__((unused))) { active_index= idx; return 0; } virtual int index_end() { active_index= MAX_KEY; return 0; } /** 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)= 0; virtual int rnd_end() { return 0; } virtual int write_row(unsigned char *buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int update_row(const unsigned char *old_data __attribute__((unused)), unsigned char *new_data __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int delete_row(const unsigned char *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 0; } virtual Table_flags table_flags(void) const= 0; /** 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(Session *session __attribute__((unused)), int lock_type __attribute__((unused))) { return 0; } virtual void release_auto_increment(void) { return; }; /** admin commands - called from mysql_admin_table */ virtual int check_for_upgrade(HA_CHECK_OPT *check_opt __attribute__((unused))) { return 0; } virtual int check(Session* session __attribute__((unused)), HA_CHECK_OPT* check_opt __attribute__((unused))) { 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(Session* session __attribute__((unused)), HA_CHECK_OPT* check_opt __attribute__((unused))) { return HA_ADMIN_NOT_IMPLEMENTED; } virtual void start_bulk_insert(ha_rows rows __attribute__((unused))) {} virtual int end_bulk_insert(void) { return 0; } virtual int index_read(unsigned char * buf __attribute__((unused)), const unsigned char * key __attribute__((unused)), uint32_t key_len __attribute__((unused)), enum ha_rkey_function find_flag __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int index_read_last(unsigned char * buf __attribute__((unused)), const unsigned char * key __attribute__((unused)), uint32_t key_len __attribute__((unused))) { 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 unsigned char *old_data __attribute__((unused)), unsigned char *new_data __attribute__((unused)), uint32_t *dup_key_found __attribute__((unused))) { 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(void) { return (my_errno=HA_ERR_WRONG_COMMAND); } /** Reset the auto-increment counter to the given value, i.e. the next row inserted will get the given value. This is called e.g. after TRUNCATE is emulated by doing a 'DELETE FROM t'. HA_ERR_WRONG_COMMAND is returned by storage engines that don't support this operation. */ virtual int reset_auto_increment(uint64_t value __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int optimize(Session* session __attribute__((unused)), HA_CHECK_OPT* check_opt __attribute__((unused))) { return HA_ADMIN_NOT_IMPLEMENTED; } virtual int analyze(Session* session __attribute__((unused)), HA_CHECK_OPT* check_opt __attribute__((unused))) { return HA_ADMIN_NOT_IMPLEMENTED; } virtual bool check_and_repair(Session *session __attribute__((unused))) { return true; } virtual int disable_indexes(uint32_t mode __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int enable_indexes(uint32_t mode __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } virtual int discard_or_import_tablespace(bool discard __attribute__((unused))) { return (my_errno=HA_ERR_WRONG_COMMAND); } virtual void prepare_for_alter(void) { return; } virtual void drop_table(const char *name); virtual int create(const char *name __attribute__((unused)), Table *form __attribute__((unused)), HA_CREATE_INFO *info __attribute__((unused)))=0; virtual int create_handler_files(const char *name __attribute__((unused)), const char *old_name __attribute__((unused)), int action_flag __attribute__((unused)), HA_CREATE_INFO *info __attribute__((unused))) { return false; } }; /** A Disk-Sweep MRR interface implementation This implementation makes range (and, in the future, 'ref') scans to read table rows in disk sweeps. Currently it is used by MyISAM and InnoDB. Potentially it can be used with any table handler that has non-clustered indexes and on-disk rows. */ class DsMrr_impl { public: typedef void (handler::*range_check_toggle_func_t)(bool on); DsMrr_impl() : h2(NULL) {}; handler *h; /* The "owner" handler object. It is used for scanning the index */ Table *table; /* Always equal to h->table */ private: /* Secondary handler object. It is used to retrieve full table rows by calling rnd_pos(). */ handler *h2; /* Buffer to store rowids, or (rowid, range_id) pairs */ unsigned char *rowids_buf; unsigned char *rowids_buf_cur; /* Current position when reading/writing */ unsigned char *rowids_buf_last; /* When reading: end of used buffer space */ unsigned char *rowids_buf_end; /* End of the buffer */ bool dsmrr_eof; /* true <=> We have reached EOF when reading index tuples */ /* true <=> need range association, buffer holds {rowid, range_id} pairs */ bool is_mrr_assoc; bool use_default_impl; /* true <=> shortcut all calls to default MRR impl */ public: void init(handler *h_arg, Table *table_arg) { h= h_arg; table= table_arg; } int dsmrr_init(handler *h, KEY *key, RANGE_SEQ_IF *seq_funcs, void *seq_init_param, uint32_t n_ranges, uint32_t mode, HANDLER_BUFFER *buf); void dsmrr_close(); int dsmrr_fill_buffer(handler *h); int dsmrr_next(handler *h, char **range_info); int dsmrr_info(uint32_t keyno, uint32_t n_ranges, uint32_t keys, uint32_t *bufsz, uint32_t *flags, COST_VECT *cost); ha_rows dsmrr_info_const(uint32_t keyno, RANGE_SEQ_IF *seq, void *seq_init_param, uint32_t n_ranges, uint32_t *bufsz, uint32_t *flags, COST_VECT *cost); private: bool key_uses_partial_cols(uint32_t keyno); bool choose_mrr_impl(uint32_t keyno, ha_rows rows, uint32_t *flags, uint32_t *bufsz, COST_VECT *cost); bool get_disk_sweep_mrr_cost(uint32_t keynr, ha_rows rows, uint32_t flags, uint32_t *buffer_size, COST_VECT *cost); }; extern const char *ha_row_type[]; extern const char *tx_isolation_names[]; extern const char *binlog_format_names[]; extern TYPELIB tx_isolation_typelib; extern TYPELIB myisam_stats_method_typelib; extern uint32_t total_ha, total_ha_2pc; /* Wrapper functions */ #define ha_commit(session) (ha_commit_trans((session), true)) #define ha_rollback(session) (ha_rollback_trans((session), true)) /* lookups */ handlerton *ha_default_handlerton(Session *session); plugin_ref ha_resolve_by_name(Session *session, const LEX_STRING *name); plugin_ref ha_lock_engine(Session *session, handlerton *hton); handlerton *ha_resolve_by_legacy_type(Session *session, enum legacy_db_type db_type); handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc, handlerton *db_type); handlerton *ha_checktype(Session *session, enum legacy_db_type database_type, bool no_substitute, bool report_error); static inline enum legacy_db_type ha_legacy_type(const handlerton *db_type) { return (db_type == NULL) ? DB_TYPE_UNKNOWN : db_type->db_type; } static inline const char *ha_resolve_storage_engine_name(const handlerton *db_type) { return db_type == NULL ? "UNKNOWN" : hton2plugin[db_type->slot]->name.str; } static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint32_t flag) { return db_type == NULL ? false : test(db_type->flags & flag); } static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) { return (db_type && db_type->create) ? (db_type->state == SHOW_OPTION_YES) : false; } /* basic stuff */ int ha_init_errors(void); int ha_init(void); int ha_end(void); int ha_initialize_handlerton(st_plugin_int *plugin); int ha_finalize_handlerton(st_plugin_int *plugin); TYPELIB *ha_known_exts(void); void ha_close_connection(Session* session); bool ha_flush_logs(handlerton *db_type); void ha_drop_database(char* path); int ha_create_table(Session *session, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, bool update_create_info); int ha_delete_table(Session *session, handlerton *db_type, const char *path, const char *db, const char *alias, bool generate_warning); /* statistics and info */ bool ha_show_status(Session *session, handlerton *db_type, enum ha_stat_type stat); /* discovery */ int ha_create_table_from_engine(Session* session, const char *db, const char *name); int ha_discover(Session* session, const char* dbname, const char* name, unsigned char** frmblob, size_t* frmlen); int ha_find_files(Session *session,const char *db,const char *path, const char *wild, bool dir, List* files); int ha_table_exists_in_engine(Session* session, const char* db, const char* name); /* key cache */ extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache); int ha_resize_key_cache(KEY_CACHE *key_cache); int ha_change_key_cache_param(KEY_CACHE *key_cache); int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache); int ha_end_key_cache(KEY_CACHE *key_cache); /* report to InnoDB that control passes to the client */ int ha_release_temporary_latches(Session *session); /* transactions: interface to handlerton functions */ int ha_start_consistent_snapshot(Session *session); int ha_commit_or_rollback_by_xid(XID *xid, bool commit); int ha_commit_one_phase(Session *session, bool all); int ha_rollback_trans(Session *session, bool all); int ha_prepare(Session *session); int ha_recover(HASH *commit_list); /* transactions: these functions never call handlerton functions directly */ int ha_commit_trans(Session *session, bool all); int ha_autocommit_or_rollback(Session *session, int error); int ha_enable_transaction(Session *session, bool on); /* savepoints */ int ha_rollback_to_savepoint(Session *session, SAVEPOINT *sv); int ha_savepoint(Session *session, SAVEPOINT *sv); int ha_release_savepoint(Session *session, SAVEPOINT *sv); /* these are called by storage engines */ void trans_register_ha(Session *session, bool all, handlerton *ht); void table_case_convert(char * name, uint32_t length); const char *table_case_name(HA_CREATE_INFO *info, const char *name); extern char reg_ext[FN_EXTLEN]; extern uint32_t reg_ext_length; extern ulong specialflag; extern uint32_t lower_case_table_names; uint32_t filename_to_tablename(const char *from, char *to, uint32_t to_length); uint32_t tablename_to_filename(const char *from, char *to, uint32_t to_length); /* Storage engine has to assume the transaction will end up with 2pc if - there is more than one 2pc-capable storage engine available - in the current transaction 2pc was not disabled yet */ #define trans_need_2pc(session, all) ((total_ha_2pc > 1) && \ !((all ? &session->transaction.all : &session->transaction.stmt)->no_2pc)) #endif /* DRIZZLED_HANDLER_H */