diff options
Diffstat (limited to 'sql/handler.cc')
-rw-r--r-- | sql/handler.cc | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/sql/handler.cc b/sql/handler.cc new file mode 100644 index 00000000000..817e3fa79dc --- /dev/null +++ b/sql/handler.cc @@ -0,0 +1,614 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* Handler-calling-functions */ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "ha_heap.h" +#include "ha_myisam.h" +#include "ha_myisammrg.h" +#ifndef NO_ISAM +#include "ha_isam.h" +#include "ha_isammrg.h" +#endif +#ifdef HAVE_BERKELEY_DB +#include "ha_berkeley.h" +#endif +#include <myisampack.h> +#include <errno.h> + + /* static functions defined in this file */ + +static void NEAR_F set_form_timestamp(TABLE *table, byte *record); +static int NEAR_F delete_file(const char *name,const char *ext,int extflag); + +ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, + ha_read_key_count, ha_read_next_count, ha_read_prev_count, + ha_read_first_count, ha_read_last_count, + ha_read_rnd_count, ha_read_rnd_next_count; + +const char *ha_table_type[] = { + "", "DIAB_ISAM","HASH","MISAM","PISAM","RMS_ISAM","HEAP", "ISAM", + "MRG_ISAM","MYISAM", "MRG_MYISAM", "BERKELEY_DB?", "?", "?",NullS +}; + +const char *ha_row_type[] = { + "", "FIXED", "DYNAMIC", "COMPRESSED","?","?","?" +}; + +TYPELIB ha_table_typelib= {array_elements(ha_table_type)-4,"", + ha_table_type+1}; + + + /* Use other database handler if databasehandler is not incompiled */ + +enum db_type ha_checktype(enum db_type database_type) +{ + switch (database_type) { +#ifdef HAVE_BERKELEY_DB + case DB_TYPE_BERKELEY_DB: + return(berkeley_skip ? DB_TYPE_MYISAM : database_type); +#endif +#ifndef NO_HASH + case DB_TYPE_HASH: +#endif +#ifndef NO_MERGE + case DB_TYPE_MRG_ISAM: +#endif +#ifndef NO_ISAM + case DB_TYPE_ISAM: +#endif + case DB_TYPE_HEAP: + case DB_TYPE_MYISAM: + case DB_TYPE_MRG_MYISAM: + return (database_type); /* Database exists on system */ + default: + break; + } + return(DB_TYPE_MYISAM); /* Use this as default */ +} /* ha_checktype */ + + +handler *get_new_handler(TABLE *table, enum db_type db_type) +{ + switch (db_type) { +#ifndef NO_HASH + return new ha_hash(table); +#endif +#ifndef NO_MERGE + case DB_TYPE_MRG_ISAM: + return new ha_isammrg(table); +#endif +#ifndef NO_ISAM + case DB_TYPE_ISAM: + return new ha_isam(table); +#endif +#ifdef HAVE_BERKELEY_DB + case DB_TYPE_BERKELEY_DB: + return new ha_berkeley(table); +#endif + case DB_TYPE_HEAP: + return new ha_heap(table); + case DB_TYPE_MYISAM: + default: // should never happen + return new ha_myisam(table); + case DB_TYPE_MRG_MYISAM: + return new ha_myisammrg(table); + } +} + +int ha_init() +{ +#ifdef HAVE_BERKELEY_DB + if (!berkeley_skip) + { + int error; + if ((error=berkeley_init())) + return error; + } +#endif + return 0; +} + + /* close, flush or restart databases */ + /* Ignore this for other databases than ours */ + +int ha_panic(enum ha_panic_function flag) +{ + int error=0; +#ifndef NO_MERGE + error|=mrg_panic(flag); +#endif +#ifndef NO_HASH + error|=h_panic(flag); /* fix hash */ +#endif + error|=heap_panic(flag); + error|=nisam_panic(flag); + error|=mi_panic(flag); + error|=myrg_panic(flag); +#ifdef HAVE_BERKELEY_DB + if (!berkeley_skip) + error|=berkeley_end(); +#endif + return error; +} /* ha_panic */ + + +int ha_autocommit_or_rollback(THD *thd, int error) +{ + DBUG_ENTER("ha_autocommit_or_rollback"); +#ifdef HAVE_BERKELEY_DB + if ((thd->options & OPTION_AUTO_COMMIT) && !thd->locked_tables) + { + if (!error) + { + if (ha_commit(thd)) + error=1; + } + else + (void) ha_rollback(thd); + } +#endif + DBUG_RETURN(error); +} + +int ha_commit(THD *thd) +{ + int error=0; + DBUG_ENTER("commit"); +#ifdef HAVE_BERKELEY_DB + if (thd->transaction.bdb_tid) + { + int error=berkeley_commit(thd); + if (error) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); + error=1; + } + } +#endif + DBUG_RETURN(error); +} + +int ha_rollback(THD *thd) +{ + int error=0; + DBUG_ENTER("commit"); +#ifdef HAVE_BERKELEY_DB + if (thd->transaction.bdb_tid) + { + int error=berkeley_rollback(thd); + if (error) + { + my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error); + error=1; + } + } +#endif + DBUG_RETURN(error); +} + + +bool ha_flush_logs() +{ + bool result=0; +#ifdef HAVE_BERKELEY_DB + if (!berkeley_skip && berkeley_flush_logs()) + result=1; +#endif + return result; +} + + +int ha_delete_table(enum db_type table_type, const char *path) +{ + handler *file=get_new_handler((TABLE*) 0, table_type); + if (!file) + return -1; + int error=file->delete_table(path); + delete file; + return error; +} + +void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos) +{ + switch (pack_length) { +#if SIZEOF_OFF_T > 4 + case 8: mi_int8store(buff,pos); break; + case 7: mi_int7store(buff,pos); break; + case 6: mi_int6store(buff,pos); break; + case 5: mi_int5store(buff,pos); break; +#endif + case 4: mi_int4store(buff,pos); break; + case 3: mi_int3store(buff,pos); break; + case 2: mi_int2store(buff,(uint) pos); break; + case 1: buff[0]= (uchar) pos; break; + } + return; +} + +my_off_t ha_get_ptr(byte *ptr, uint pack_length) +{ + my_off_t pos; + switch (pack_length) { +#if SIZEOF_OFF_T > 4 + case 8: + pos= (my_off_t) mi_uint8korr(ptr); + break; + case 7: + pos= (my_off_t) mi_uint7korr(ptr); + break; + case 6: + pos= (my_off_t) mi_uint6korr(ptr); + break; + case 5: + pos= (my_off_t) mi_uint5korr(ptr); + break; +#endif + case 4: + pos= (my_off_t) mi_uint4korr(ptr); + break; + case 3: + pos= (my_off_t) mi_uint3korr(ptr); + break; + case 2: + pos= (my_off_t) mi_uint2korr(ptr); + break; + case 1: + pos= (my_off_t) mi_uint2korr(ptr); + break; + default: + pos=0; // Impossible + break; + } + return pos; +} + +/**************************************************************************** +** General handler functions +****************************************************************************/ + + /* Open database-handler. Try O_RDONLY if can't open as O_RDWR */ + /* Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */ + +int handler::ha_open(const char *name, int mode, int test_if_locked) +{ + int error; + DBUG_ENTER("handler::open"); + DBUG_PRINT("enter",("db_type: %d db_stat: %d mode: %d lock_test: %d", + table->db_type, table->db_stat, mode, test_if_locked)); + + if ((error=open(name,mode,test_if_locked))) + { + if ((error == EACCES || error == EROFS) && mode == O_RDWR && + (table->db_stat & HA_TRY_READ_ONLY)) + { + table->db_stat|=HA_READ_ONLY; + error=open(name,O_RDONLY,test_if_locked); + } + } + if (error) + { + my_errno=error; /* Safeguard */ + DBUG_PRINT("error",("error: %d errno: %d",error,errno)); + } + else + { + if (table->db_options_in_use & HA_OPTION_READ_ONLY_DATA) + table->db_stat|=HA_READ_ONLY; + } + if (!error) + { + + if (!(ref=(byte*) my_malloc(ALIGN_SIZE(ref_length)*2,MYF(0)))) + { + close(); + error=HA_ERR_OUT_OF_MEM; + } + else + dupp_ref=ref+ALIGN_SIZE(ref_length); + } + DBUG_RETURN(error); +} + +int handler::check(THD* thd, HA_CHECK_OPT* check_opt) +{ + return HA_CHECK_NOT_IMPLEMENTED; +} + +int handler::repair(THD* thd, HA_CHECK_OPT* check_opt) +{ + return HA_REPAIR_NOT_IMPLEMENTED; +} + +int handler::optimize(THD* thd) +{ + return HA_OPTIMIZE_NOT_IMPLEMENTED; +} + +int handler::analyze(THD* thd) +{ + return HA_ANALYZE_NOT_IMPLEMENTED; +} + + /* Read first row from a table */ + +int handler::rnd_first(byte * buf) +{ + register int error; + DBUG_ENTER("handler::rnd_first"); + + statistic_increment(ha_read_first_count,&LOCK_status); + (void) rnd_init(); + while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; + (void) rnd_end(); + DBUG_RETURN(error); +} + + +/* + The following function is only needed for tables that may be temporary tables + during joins +*/ + +int handler::restart_rnd_next(byte *buf, byte *pos) +{ + return HA_ERR_WRONG_COMMAND; +} + + + /* Set a timestamp in record */ + +void handler::update_timestamp(byte *record) +{ + long skr= (long) current_thd->query_start(); +#ifdef WORDS_BIGENDIAN + if (table->db_low_byte_first) + { + int4store(record,skr); + } + else +#endif + longstore(record,skr); + return; +} + + /* Updates field with field_type NEXT_NUMBER according to following: + ** if field = 0 change field to the next free key in database. + */ + +void handler::update_auto_increment() +{ + longlong nr; + THD *thd; + DBUG_ENTER("update_auto_increment"); + if (table->next_number_field->val_int() != 0) + DBUG_VOID_RETURN; + thd=current_thd; + if ((nr=thd->next_insert_id)) + thd->next_insert_id=0; // Clear after use + else + nr=get_auto_increment(); + thd->insert_id((ulonglong) nr); + table->next_number_field->store(nr); + DBUG_VOID_RETURN; +} + + +longlong handler::get_auto_increment() +{ + longlong nr; + int error; + (void) extra(HA_EXTRA_KEYREAD); + index_init(table->next_number_index); + error=index_last(table->record[1]); + if (error) + nr=1; + else + nr=(longlong) table->next_number_field-> + val_int_offset(table->rec_buff_length)+1; + (void) extra(HA_EXTRA_NO_KEYREAD); + index_end(); + return nr; +} + + /* Print error that we got from handler function */ + +void handler::print_error(int error, myf errflag) +{ + DBUG_ENTER("print_error"); + DBUG_PRINT("enter",("error: %d",error)); + + int textno=ER_GET_ERRNO; + switch (error) { + case EAGAIN: + textno=ER_FILE_USED; + break; + case ENOENT: + textno=ER_FILE_NOT_FOUND; + break; + case HA_ERR_KEY_NOT_FOUND: + case HA_ERR_NO_ACTIVE_RECORD: + case HA_ERR_END_OF_FILE: + textno=ER_KEY_NOT_FOUND; + break; + case HA_ERR_WRONG_TABLE_DEF: + textno=ER_WRONG_MRG_TABLE; + break; + case HA_ERR_FOUND_DUPP_KEY: + { + uint key_nr=get_dup_key(error); + if ((int) key_nr >= 0) + { + /* Write the dupplicated key in the error message */ + char key[MAX_KEY_LENGTH]; + String str(key,sizeof(key)); + key_unpack(&str,table,(uint) key_nr); + uint max_length=MYSQL_ERRMSG_SIZE-strlen(ER(ER_DUP_ENTRY)); + if (str.length() >= max_length) + { + str.length(max_length-4); + str.append("..."); + } + my_error(ER_DUP_ENTRY,MYF(0),str.c_ptr(),key_nr+1); + DBUG_VOID_RETURN; + } + textno=ER_DUP_KEY; + break; + } + case HA_ERR_FOUND_DUPP_UNIQUE: + textno=ER_DUP_UNIQUE; + break; + case HA_ERR_RECORD_CHANGED: + textno=ER_CHECKREAD; + break; + case HA_ERR_CRASHED: + textno=ER_NOT_KEYFILE; + break; + case HA_ERR_OUT_OF_MEM: + my_error(ER_OUT_OF_RESOURCES,errflag); + DBUG_VOID_RETURN; + case HA_ERR_WRONG_COMMAND: + textno=ER_ILLEGAL_HA; + break; + case HA_ERR_OLD_FILE: + textno=ER_OLD_KEYFILE; + break; + case HA_ERR_UNSUPPORTED: + textno=ER_UNSUPPORTED_EXTENSION; + break; + case HA_ERR_RECORD_FILE_FULL: + textno=ER_RECORD_FILE_FULL; + break; + default: + { + my_error(ER_GET_ERRNO,errflag,error); + DBUG_VOID_RETURN; + } + } + my_error(textno,errflag,table->table_name,error); + DBUG_VOID_RETURN; +} + + /* Return key if error because of duplicated keys */ + +uint handler::get_dup_key(int error) +{ + DBUG_ENTER("key_error"); + table->file->errkey = (uint) -1; + if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE) + info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK); + DBUG_RETURN(table->file->errkey); +} + +int handler::delete_table(const char *name) +{ + for (const char **ext=bas_ext(); *ext ; ext++) + { + if (delete_file(name,*ext,2)) + return my_errno; + } + return 0; +} + + +int handler::rename_table(const char * from, const char * to) +{ + DBUG_ENTER("handler::rename_table"); + for (const char **ext=bas_ext(); *ext ; ext++) + { + if (rename_file_ext(from,to,*ext)) + DBUG_RETURN(my_errno); + } + DBUG_RETURN(0); +} + + +int handler::index_next_same(byte *buf, const byte *key, uint keylen) +{ + int error; + if (!(error=index_next(buf))) + { + if (key_cmp(table, key, active_index, keylen)) + { + table->status=STATUS_NOT_FOUND; + error=HA_ERR_END_OF_FILE; + } + } + return error; +} + + +/* + The following is only needed if we would like to use the database + for internal temporary tables +*/ + +int handler::delete_all_rows() +{ + return (my_errno=HA_ERR_WRONG_COMMAND); +} + +/**************************************************************************** +** Some general functions that isn't in the handler class +****************************************************************************/ + + /* Initiates table-file and calls apropriate database-creator */ + /* Returns 1 if something got wrong */ + +int ha_create_table(const char *name, HA_CREATE_INFO *create_info, + bool update_create_info) + +{ + int error; + TABLE table; + DBUG_ENTER("ha_create_table"); + + if (openfrm(name,"",0,(uint) READ_ALL,&table)) + DBUG_RETURN(1); + if (update_create_info) + { + update_create_info_from_table(create_info, &table); + if (table.file->option_flag() & HA_DROP_BEFORE_CREATE) + table.file->delete_table(name); // Needed for BDB tables + } + error=table.file->create(name,&table,create_info); + VOID(closefrm(&table)); + if (error) + my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno); + DBUG_RETURN(error != 0); +} + + /* Use key cacheing on all databases */ + +void ha_key_cache(void) +{ + if (keybuff_size) + (void) init_key_cache(keybuff_size,0); +} /* ha_key_cache */ + + +static int NEAR_F delete_file(const char *name,const char *ext,int extflag) +{ + char buff[FN_REFLEN]; + VOID(fn_format(buff,name,"",ext,extflag | 4)); + return(my_delete(buff,MYF(MY_WME))); +} |