summaryrefslogtreecommitdiff
path: root/sql/handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/handler.cc')
-rw-r--r--sql/handler.cc712
1 files changed, 548 insertions, 164 deletions
diff --git a/sql/handler.cc b/sql/handler.cc
index 9eb129fab45..39a6296a525 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -32,9 +32,21 @@
#ifdef HAVE_BERKELEY_DB
#include "ha_berkeley.h"
#endif
+#ifdef HAVE_EXAMPLE_DB
+#include "examples/ha_example.h"
+#endif
+#ifdef HAVE_ARCHIVE_DB
+#include "examples/ha_archive.h"
+#endif
+#ifdef HAVE_CSV_DB
+#include "examples/ha_tina.h"
+#endif
#ifdef HAVE_INNOBASE_DB
#include "ha_innodb.h"
#endif
+#ifdef HAVE_NDBCLUSTER_DB
+#include "ha_ndbcluster.h"
+#endif
#include <myisampack.h>
#include <errno.h>
@@ -46,16 +58,45 @@ 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_commit_count, ha_rollback_count,
- ha_read_rnd_count, ha_read_rnd_next_count;
+ ha_read_rnd_count, ha_read_rnd_next_count, ha_discover_count;
-const char *ha_table_type[] = {
- "", "DIAB_ISAM","HASH","MISAM","PISAM","RMS_ISAM","HEAP", "ISAM",
- "MRG_ISAM","MYISAM", "MRG_MYISAM", "BDB", "INNODB", "GEMINI", "?", "?",NullS
-};
+static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES;
-TYPELIB ha_table_typelib=
+struct show_table_type_st sys_table_types[]=
{
- array_elements(ha_table_type)-3, "", ha_table_type
+ {"MyISAM", &have_yes,
+ "Default engine as of MySQL 3.23 with great performance", DB_TYPE_MYISAM},
+ {"HEAP", &have_yes,
+ "Alias for MEMORY", DB_TYPE_HEAP},
+ {"MEMORY", &have_yes,
+ "Hash based, stored in memory, useful for temporary tables", DB_TYPE_HEAP},
+ {"MERGE", &have_yes,
+ "Collection of identical MyISAM tables", DB_TYPE_MRG_MYISAM},
+ {"MRG_MYISAM",&have_yes,
+ "Alias for MERGE", DB_TYPE_MRG_MYISAM},
+ {"ISAM", &have_isam,
+ "Obsolete storage engine, now replaced by MyISAM", DB_TYPE_ISAM},
+ {"MRG_ISAM", &have_isam,
+ "Obsolete storage engine, now replaced by MERGE", DB_TYPE_MRG_ISAM},
+ {"InnoDB", &have_innodb,
+ "Supports transactions, row-level locking, and foreign keys", DB_TYPE_INNODB},
+ {"INNOBASE", &have_innodb,
+ "Alias for INNODB", DB_TYPE_INNODB},
+ {"BDB", &have_berkeley_db,
+ "Supports transactions and page-level locking", DB_TYPE_BERKELEY_DB},
+ {"BERKELEYDB",&have_berkeley_db,
+ "Alias for BDB", DB_TYPE_BERKELEY_DB},
+ {"NDBCLUSTER", &have_ndbcluster,
+ "Clustered, fault-tolerant, memory-based tables", DB_TYPE_NDBCLUSTER},
+ {"NDB", &have_ndbcluster,
+ "Alias for NDBCLUSTER", DB_TYPE_NDBCLUSTER},
+ {"EXAMPLE",&have_example_db,
+ "Example storage engine", DB_TYPE_EXAMPLE_DB},
+ {"ARCHIVE",&have_archive_db,
+ "Archive storage engine", DB_TYPE_ARCHIVE_DB},
+ {"CSV",&have_csv_db,
+ "CSV storage engine", DB_TYPE_CSV_DB},
+ {NullS, NULL, NullS, DB_TYPE_UNKNOWN}
};
const char *ha_row_type[] = {
@@ -68,34 +109,63 @@ const char *tx_isolation_names[] =
TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
tx_isolation_names};
+enum db_type ha_resolve_by_name(const char *name, uint namelen)
+{
+ THD *thd=current_thd;
+ if (thd && !my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) {
+ return (enum db_type) thd->variables.table_type;
+ }
+
+ show_table_type_st *types;
+ for (types= sys_table_types; types->type; types++)
+ {
+ if (!my_strcasecmp(&my_charset_latin1, name, types->type))
+ return (enum db_type) types->db_type;
+ }
+ return DB_TYPE_UNKNOWN;
+}
+
+const char *ha_get_storage_engine(enum db_type db_type)
+{
+ show_table_type_st *types;
+ for (types= sys_table_types; types->type; types++)
+ {
+ if (db_type == types->db_type)
+ return types->type;
+ }
+
+ return "none";
+}
+
/* Use other database handler if databasehandler is not incompiled */
enum db_type ha_checktype(enum db_type database_type)
{
+ show_table_type_st *types;
+ for (types= sys_table_types; types->type; types++)
+ {
+ if ((database_type == types->db_type) &&
+ (*types->value == SHOW_OPTION_YES))
+ return database_type;
+ }
+
switch (database_type) {
-#ifdef HAVE_BERKELEY_DB
- case DB_TYPE_BERKELEY_DB:
- return(berkeley_skip ? DB_TYPE_MYISAM : database_type);
-#endif
-#ifdef HAVE_INNOBASE_DB
- case DB_TYPE_INNODB:
- return(innodb_skip ? DB_TYPE_MYISAM : database_type);
-#endif
#ifndef NO_HASH
case DB_TYPE_HASH:
+ return (database_type);
#endif
-#ifdef HAVE_ISAM
- case DB_TYPE_ISAM:
case DB_TYPE_MRG_ISAM:
-#endif
- case DB_TYPE_HEAP:
- case DB_TYPE_MYISAM:
- case DB_TYPE_MRG_MYISAM:
- return (database_type); /* Database exists on system */
+ return (DB_TYPE_MRG_MYISAM);
default:
break;
}
- return(DB_TYPE_MYISAM); /* Use this as default */
+
+ return
+ DB_TYPE_UNKNOWN != (enum db_type) current_thd->variables.table_type ?
+ (enum db_type) current_thd->variables.table_type :
+ DB_TYPE_UNKNOWN != (enum db_type) global_system_variables.table_type ?
+ (enum db_type) global_system_variables.table_type :
+ DB_TYPE_MYISAM;
} /* ha_checktype */
@@ -103,13 +173,17 @@ handler *get_new_handler(TABLE *table, enum db_type db_type)
{
switch (db_type) {
#ifndef NO_HASH
- return new ha_hash(table);
+ case DB_TYPE_HASH:
+ return new ha_hash(table);
#endif
#ifdef HAVE_ISAM
case DB_TYPE_MRG_ISAM:
return new ha_isammrg(table);
case DB_TYPE_ISAM:
return new ha_isam(table);
+#else
+ case DB_TYPE_MRG_ISAM:
+ return new ha_myisammrg(table);
#endif
#ifdef HAVE_BERKELEY_DB
case DB_TYPE_BERKELEY_DB:
@@ -119,6 +193,22 @@ handler *get_new_handler(TABLE *table, enum db_type db_type)
case DB_TYPE_INNODB:
return new ha_innobase(table);
#endif
+#ifdef HAVE_EXAMPLE_DB
+ case DB_TYPE_EXAMPLE_DB:
+ return new ha_example(table);
+#endif
+#ifdef HAVE_ARCHIVE_DB
+ case DB_TYPE_ARCHIVE_DB:
+ return new ha_archive(table);
+#endif
+#ifdef HAVE_CSV_DB
+ case DB_TYPE_CSV_DB:
+ return new ha_tina(table);
+#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ case DB_TYPE_NDBCLUSTER:
+ return new ha_ndbcluster(table);
+#endif
case DB_TYPE_HEAP:
return new ha_heap(table);
default: // should never happen
@@ -136,32 +226,56 @@ handler *get_new_handler(TABLE *table, enum db_type db_type)
}
}
+bool ha_caching_allowed(THD* thd, char* table_key,
+ uint key_length, uint8 cache_type)
+{
+#ifdef HAVE_INNOBASE_DB
+ if (cache_type == HA_CACHE_TBL_ASKTRANSACT)
+ return innobase_query_caching_of_table_permitted(thd, table_key, key_length);
+#endif
+ return 1;
+}
+
int ha_init()
{
+ int error= 0;
#ifdef HAVE_BERKELEY_DB
- if (!berkeley_skip)
+ if (have_berkeley_db == SHOW_OPTION_YES)
{
- int error;
- if ((error=berkeley_init()))
- return error;
- if (!berkeley_skip) // If we couldn't use handler
- opt_using_transactions=1;
+ if (berkeley_init())
+ {
+ have_berkeley_db= SHOW_OPTION_DISABLED; // If we couldn't use handler
+ error= 1;
+ }
else
- have_berkeley_db=SHOW_OPTION_DISABLED;
+ opt_using_transactions=1;
}
#endif
#ifdef HAVE_INNOBASE_DB
- if (!innodb_skip)
+ if (have_innodb == SHOW_OPTION_YES)
{
if (innobase_init())
- return -1;
- if (!innodb_skip) // If we couldn't use handler
+ {
+ have_innodb= SHOW_OPTION_DISABLED; // If we couldn't use handler
+ error= 1;
+ }
+ else
opt_using_transactions=1;
+ }
+#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ if (have_ndbcluster == SHOW_OPTION_YES)
+ {
+ if (ndbcluster_init())
+ {
+ have_ndbcluster= SHOW_OPTION_DISABLED;
+ error= 1;
+ }
else
- have_innodb=SHOW_OPTION_DISABLED;
+ opt_using_transactions=1;
}
#endif
- return 0;
+ return error;
}
/* close, flush or restart databases */
@@ -181,30 +295,42 @@ int ha_panic(enum ha_panic_function flag)
error|=mi_panic(flag);
error|=myrg_panic(flag);
#ifdef HAVE_BERKELEY_DB
- if (!berkeley_skip)
+ if (have_berkeley_db == SHOW_OPTION_YES)
error|=berkeley_end();
#endif
#ifdef HAVE_INNOBASE_DB
- if (!innodb_skip)
+ if (have_innodb == SHOW_OPTION_YES)
error|=innobase_end();
#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ if (have_ndbcluster == SHOW_OPTION_YES)
+ error|=ndbcluster_end();
+#endif
return error;
} /* ha_panic */
void ha_drop_database(char* path)
{
#ifdef HAVE_INNOBASE_DB
- if (!innodb_skip)
+ if (have_innodb == SHOW_OPTION_YES)
innobase_drop_database(path);
#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ if (have_ndbcluster == SHOW_OPTION_YES)
+ ndbcluster_drop_database(path);
+#endif
}
void ha_close_connection(THD* thd)
{
#ifdef HAVE_INNOBASE_DB
- if (!innodb_skip)
+ if (have_innodb == SHOW_OPTION_YES)
innobase_close_connection(thd);
#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ if (have_ndbcluster == SHOW_OPTION_YES)
+ ndbcluster_close_connection(thd);
+#endif
}
/*
@@ -266,17 +392,25 @@ int ha_report_binlog_offset_and_commit(THD *thd,
#ifdef HAVE_INNOBASE_DB
THD_TRANS *trans;
trans = &thd->transaction.all;
- if (trans->innobase_tid)
+ if (trans->innodb_active_trans)
{
+ /*
+ If we updated some InnoDB tables (innodb_active_trans is true), the
+ binlog coords will be reported into InnoDB during the InnoDB commit
+ (innobase_report_binlog_offset_and_commit). But if we updated only
+ non-InnoDB tables, we need an explicit call to report it.
+ */
if ((error=innobase_report_binlog_offset_and_commit(thd,
- trans->innobase_tid,
- log_file_name,
- end_offset)))
+ trans->innobase_tid,
+ log_file_name,
+ end_offset)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), error);
error=1;
}
}
+ else if (opt_innodb_safe_binlog) // Don't report if not useful
+ innobase_store_binlog_offset_and_flush_log(log_file_name, end_offset);
#endif
return error;
}
@@ -342,8 +476,9 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans)
#ifdef USING_TRANSACTIONS
if (opt_using_transactions)
{
+ bool operation_done= 0;
bool operation_done= 0, need_start_waiters= 0;
- bool transaction_commited= 0;
+
/* If transaction has done some updates to tables */
if (trans == &thd->transaction.all &&
my_b_tell(&thd->transaction.trans_log))
@@ -362,11 +497,35 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans)
if (mysql_bin_log.is_open())
{
mysql_bin_log.write(thd, &thd->transaction.trans_log, 1);
+ statistic_increment(binlog_cache_use, &LOCK_status);
+ if (thd->transaction.trans_log.disk_writes != 0)
+ {
+ /*
+ We have to do this after addition of trans_log to main binlog since
+ this operation can cause flushing of end of trans_log to disk.
+ */
+ statistic_increment(binlog_cache_disk_use, &LOCK_status);
+ thd->transaction.trans_log.disk_writes= 0;
+ }
reinit_io_cache(&thd->transaction.trans_log,
WRITE_CACHE, (my_off_t) 0, 0, 1);
thd->transaction.trans_log.end_of_file= max_binlog_cache_size;
}
}
+#ifdef HAVE_NDBCLUSTER_DB
+ if (trans->ndb_tid)
+ {
+ if ((error=ndbcluster_commit(thd,trans->ndb_tid)))
+ {
+ if (error == -1)
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0));
+ error=1;
+ }
+ if (trans == &thd->transaction.all)
+ operation_done= transaction_commited= 1;
+ trans->ndb_tid=0;
+ }
+#endif
#ifdef HAVE_BERKELEY_DB
if (trans->bdb_tid)
{
@@ -422,6 +581,19 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
if (opt_using_transactions)
{
bool operation_done=0;
+#ifdef HAVE_NDBCLUSTER_DB
+ if (trans->ndb_tid)
+ {
+ if ((error=ndbcluster_rollback(thd, trans->ndb_tid)))
+ {
+ if (error == -1)
+ my_error(ER_ERROR_DURING_ROLLBACK, MYF(0));
+ error=1;
+ }
+ trans->ndb_tid = 0;
+ operation_done=1;
+ }
+#endif
#ifdef HAVE_BERKELEY_DB
if (trans->bdb_tid)
{
@@ -446,17 +618,30 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
operation_done=1;
}
#endif
- if (trans == &thd->transaction.all)
+ if ((trans == &thd->transaction.all) && mysql_bin_log.is_open())
{
/*
- Update the binary log with a BEGIN/ROLLBACK block if we have cached some
- queries and we updated some non-transactional table. Such cases should
- be rare (updating a non-transactional table inside a transaction...).
+ Update the binary log with a BEGIN/ROLLBACK block if we have
+ cached some queries and we updated some non-transactional
+ table. Such cases should be rare (updating a
+ non-transactional table inside a transaction...). Count disk
+ writes to trans_log in any case.
*/
- if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
- mysql_bin_log.is_open() &&
- my_b_tell(&thd->transaction.trans_log)))
- mysql_bin_log.write(thd, &thd->transaction.trans_log, 0);
+ if (my_b_tell(&thd->transaction.trans_log))
+ {
+ if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE))
+ mysql_bin_log.write(thd, &thd->transaction.trans_log, 0);
+ statistic_increment(binlog_cache_use, &LOCK_status);
+ if (thd->transaction.trans_log.disk_writes != 0)
+ {
+ /*
+ We have to do this after addition of trans_log to main binlog since
+ this operation can cause flushing of end of trans_log to disk.
+ */
+ statistic_increment(binlog_cache_disk_use, &LOCK_status);
+ thd->transaction.trans_log.disk_writes= 0;
+ }
+ }
/* Flushed or not, empty the binlog cache */
reinit_io_cache(&thd->transaction.trans_log,
WRITE_CACHE, (my_off_t) 0, 0, 1);
@@ -484,12 +669,12 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
simply truncate the binlog cache, we lose the part of the binlog cache where
the update is. If we want to not lose it, we need to write the SAVEPOINT
command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
- is easy: it's just write at the end of the binlog cache, but the former should
- be *inserted* to the place where the user called SAVEPOINT. The solution is
- that when the user calls SAVEPOINT, we write it to the binlog cache (so no
- need to later insert it). As transactions are never intermixed in the binary log
- (i.e. they are serialized), we won't have conflicts with savepoint names when
- using mysqlbinlog or in the slave SQL thread.
+ is easy: it's just write at the end of the binlog cache, but the former
+ should be *inserted* to the place where the user called SAVEPOINT. The
+ solution is that when the user calls SAVEPOINT, we write it to the binlog
+ cache (so no need to later insert it). As transactions are never intermixed
+ in the binary log (i.e. they are serialized), we won't have conflicts with
+ savepoint names when using mysqlbinlog or in the slave SQL thread.
Then when ROLLBACK TO SAVEPOINT is called, if we updated some
non-transactional table, we don't truncate the binlog cache but instead write
ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
@@ -518,7 +703,7 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name)
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error);
error=1;
}
- else
+ else if (mysql_bin_log.is_open())
{
/*
Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
@@ -526,7 +711,6 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name)
from the SAVEPOINT command.
*/
if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
- mysql_bin_log.is_open() &&
my_b_tell(&thd->transaction.trans_log)))
{
Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE);
@@ -555,23 +739,26 @@ Return value: always 0, that is, succeeds always
int ha_savepoint(THD *thd, char *savepoint_name)
{
- my_off_t binlog_cache_pos=0;
int error=0;
DBUG_ENTER("ha_savepoint");
#ifdef USING_TRANSACTIONS
if (opt_using_transactions)
{
- binlog_cache_pos=my_b_tell(&thd->transaction.trans_log);
-#ifdef HAVE_INNOBASE_DB
- innobase_savepoint(thd,savepoint_name, binlog_cache_pos);
-#endif
- /* Write it to the binary log (see comments of ha_rollback_to_savepoint). */
+ /* Write it to the binary log (see comments of ha_rollback_to_savepoint) */
if (mysql_bin_log.is_open())
{
+#ifdef HAVE_INNOBASE_DB
+ innobase_savepoint(thd,savepoint_name,
+ my_b_tell(&thd->transaction.trans_log));
+#endif
Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE);
if (mysql_bin_log.write(&qinfo))
error= 1;
}
+#ifdef HAVE_INNOBASE_DB
+ else
+ innobase_savepoint(thd,savepoint_name,0);
+#endif
}
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
@@ -581,11 +768,13 @@ bool ha_flush_logs()
{
bool result=0;
#ifdef HAVE_BERKELEY_DB
- if (!berkeley_skip && berkeley_flush_logs())
+ if ((have_berkeley_db == SHOW_OPTION_YES) &&
+ berkeley_flush_logs())
result=1;
#endif
#ifdef HAVE_INNOBASE_DB
- if (!innodb_skip && innobase_flush_logs())
+ if ((have_innodb == SHOW_OPTION_YES) &&
+ innobase_flush_logs())
result=1;
#endif
return result;
@@ -606,7 +795,7 @@ int ha_delete_table(enum db_type table_type, const char *path)
{
/* Ensure that table handler get path in lower case */
strmov(tmp_path, path);
- casedn_str(tmp_path);
+ my_casedn_str(files_charset_info, tmp_path);
path= tmp_path;
}
int error=file->delete_table(path);
@@ -719,36 +908,6 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
DBUG_RETURN(error);
}
-int handler::check(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_NOT_IMPLEMENTED;
-}
-
-int handler::backup(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_NOT_IMPLEMENTED;
-}
-
-int handler::restore(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_NOT_IMPLEMENTED;
-}
-
-int handler::repair(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_NOT_IMPLEMENTED;
-}
-
-int handler::optimize(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_NOT_IMPLEMENTED;
-}
-
-int handler::analyze(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_NOT_IMPLEMENTED;
-}
-
/*
Read first row (only) from a table
This is never called for InnoDB or BDB tables, as these table types
@@ -766,35 +925,23 @@ int handler::read_first_row(byte * buf, uint primary_key)
If there is very few deleted rows in the table, find the first row by
scanning the table.
*/
- if (deleted < 10 || primary_key >= MAX_KEY ||
- !(index_flags(primary_key) & HA_READ_ORDER))
+ if (deleted < 10 || primary_key >= MAX_KEY)
{
- (void) rnd_init();
+ (void) ha_rnd_init(1);
while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
- (void) rnd_end();
+ (void) ha_rnd_end();
}
else
{
/* Find the first row through the primary key */
- (void) index_init(primary_key);
+ (void) ha_index_init(primary_key);
error=index_first(buf);
- (void) index_end();
+ (void) ha_index_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)
@@ -821,18 +968,24 @@ void handler::update_auto_increment()
longlong nr;
THD *thd;
DBUG_ENTER("update_auto_increment");
- if (table->next_number_field->val_int() != 0)
+ if (table->next_number_field->val_int() != 0 ||
+ table->auto_increment_field_not_null &&
+ current_thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
{
+ table->auto_increment_field_not_null= FALSE;
auto_increment_column_changed=0;
DBUG_VOID_RETURN;
}
+ table->auto_increment_field_not_null= FALSE;
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);
+ if (!table->next_number_field->store(nr))
+ thd->insert_id((ulonglong) nr);
+ else
+ thd->insert_id(table->next_number_field->val_int());
auto_increment_column_changed=1;
DBUG_VOID_RETURN;
}
@@ -901,7 +1054,7 @@ void handler::print_error(int error, myf errflag)
{
/* Write the dupplicated key in the error message */
char key[MAX_KEY_LENGTH];
- String str(key,sizeof(key));
+ String str(key,sizeof(key),system_charset_info);
key_unpack(&str,table,(uint) key_nr);
uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_DUP_ENTRY));
if (str.length() >= max_length)
@@ -968,7 +1121,21 @@ void handler::print_error(int error, myf errflag)
break;
default:
{
- my_error(ER_GET_ERRNO,errflag,error);
+ /* The error was "unknown" to this function.
+ Ask handler if it has got a message for this error */
+ bool temporary= FALSE;
+ String str;
+ temporary= get_error_message(error, &str);
+ if (!str.is_empty())
+ {
+ const char* engine= table_type();
+ if (temporary)
+ my_error(ER_GET_TEMPORARY_ERRMSG,MYF(0),error,str.ptr(),engine);
+ else
+ my_error(ER_GET_ERRMSG,MYF(0),error,str.ptr(),engine);
+ }
+ else
+ my_error(ER_GET_ERRNO,errflag,error);
DBUG_VOID_RETURN;
}
}
@@ -977,6 +1144,22 @@ void handler::print_error(int error, myf errflag)
}
+/*
+ Return an error message specific to this handler
+
+ SYNOPSIS
+ error error code previously returned by handler
+ buf Pointer to String where to add error message
+
+ Returns true if this is a temporary error
+ */
+
+bool handler::get_error_message(int error, String* buf)
+{
+ return FALSE;
+}
+
+
/* Return key if error because of duplicated keys */
uint handler::get_dup_key(int error)
@@ -1032,7 +1215,7 @@ 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))
+ if (key_cmp_if_same(table, key, active_index, keylen))
{
table->status=STATUS_NOT_FOUND;
error=HA_ERR_END_OF_FILE;
@@ -1042,24 +1225,14 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen)
}
-/*
- 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.
-*/
-
-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 */
+/*
+ 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)
@@ -1074,15 +1247,13 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
if (update_create_info)
{
update_create_info_from_table(create_info, &table);
- if (table.file->table_flags() & HA_DROP_BEFORE_CREATE)
- table.file->delete_table(name); // Needed for BDB tables
}
if (lower_case_table_names == 2 &&
!(table.file->table_flags() & HA_FILE_BASED))
{
/* Ensure that handler gets name in lower case */
strmov(name_buff, name);
- casedn_str(name_buff);
+ my_casedn_str(files_charset_info, name_buff);
name= name_buff;
}
@@ -1093,41 +1264,254 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
DBUG_RETURN(error != 0);
}
- /* Use key cacheing on all databases */
+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_with_symlink(buff,MYF(MY_WME)));
+}
-void ha_key_cache(void)
+void st_ha_check_opt::init()
{
- /*
- The following mutex is not really needed as long as keybuff_size is
- treated as a long value, but we use the mutex here to guard for future
- changes.
- */
- pthread_mutex_lock(&LOCK_global_system_variables);
- long tmp= (long) keybuff_size;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- if (tmp)
- (void) init_key_cache(tmp);
+ flags= sql_flags= 0;
+ sort_buffer_size = current_thd->variables.myisam_sort_buff_size;
}
-void ha_resize_key_cache(void)
+/*****************************************************************************
+ Key cache handling.
+
+ This code is only relevant for ISAM/MyISAM tables
+
+ key_cache->cache may be 0 only in the case where a key cache is not
+ initialized or when we where not able to init the key cache in a previous
+ call to ha_init_key_cache() (probably out of memory)
+*****************************************************************************/
+
+/* Init a key cache if it has not been initied before */
+
+
+int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
- long tmp= (long) keybuff_size;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- (void) resize_key_cache(tmp);
+ DBUG_ENTER("ha_init_key_cache");
+
+ if (!key_cache->key_cache_inited)
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ long tmp_buff_size= (long) key_cache->param_buff_size;
+ long tmp_block_size= (long) key_cache->param_block_size;
+ uint division_limit= key_cache->param_division_limit;
+ uint age_threshold= key_cache->param_age_threshold;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ DBUG_RETURN(!init_key_cache(key_cache,
+ tmp_block_size,
+ tmp_buff_size,
+ division_limit, age_threshold));
+ }
+ DBUG_RETURN(0);
}
-static int NEAR_F delete_file(const char *name,const char *ext,int extflag)
+/* Resize key cache */
+
+int ha_resize_key_cache(KEY_CACHE *key_cache)
{
- char buff[FN_REFLEN];
- VOID(fn_format(buff,name,"",ext,extflag | 4));
- return(my_delete_with_symlink(buff,MYF(MY_WME)));
+ DBUG_ENTER("ha_resize_key_cache");
+
+ if (key_cache->key_cache_inited)
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ long tmp_buff_size= (long) key_cache->param_buff_size;
+ long tmp_block_size= (long) key_cache->param_block_size;
+ uint division_limit= key_cache->param_division_limit;
+ uint age_threshold= key_cache->param_age_threshold;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size,
+ tmp_buff_size,
+ division_limit, age_threshold));
+ }
+ DBUG_RETURN(0);
}
-void st_ha_check_opt::init()
+
+/* Change parameters for key cache (like size) */
+
+int ha_change_key_cache_param(KEY_CACHE *key_cache)
{
- flags= sql_flags= 0;
- sort_buffer_size = current_thd->variables.myisam_sort_buff_size;
+ if (key_cache->key_cache_inited)
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ uint division_limit= key_cache->param_division_limit;
+ uint age_threshold= key_cache->param_age_threshold;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ change_key_cache_param(key_cache, division_limit, age_threshold);
+ }
+ return 0;
}
+
+/* Free memory allocated by a key cache */
+
+int ha_end_key_cache(KEY_CACHE *key_cache)
+{
+ end_key_cache(key_cache, 1); // Can never fail
+ return 0;
+}
+
+/* Move all tables from one key cache to another one */
+
+int ha_change_key_cache(KEY_CACHE *old_key_cache,
+ KEY_CACHE *new_key_cache)
+{
+ mi_change_key_cache(old_key_cache, new_key_cache);
+ return 0;
+}
+
+
+/*
+ Try to discover one table from handler(s)
+*/
+
+int ha_discover(const char* dbname, const char* name,
+ const void** frmblob, uint* frmlen)
+{
+ int error= 1; // Table does not exist in any handler
+ DBUG_ENTER("ha_discover");
+ DBUG_PRINT("enter", ("db: %s, name: %s", dbname, name));
+#ifdef HAVE_NDBCLUSTER_DB
+ if (have_ndbcluster == SHOW_OPTION_YES)
+ error= ndbcluster_discover(dbname, name, frmblob, frmlen);
+#endif
+ if (!error)
+ statistic_increment(ha_discover_count,&LOCK_status);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Read first row between two ranges.
+ Store ranges for future calls to read_range_next
+
+ SYNOPSIS
+ read_range_first()
+ start_key Start key. Is 0 if no min range
+ end_key End key. Is 0 if no max range
+ eq_range_arg Set to 1 if start_key == end_key
+ sorted Set to 1 if result should be sorted per key
+
+ NOTES
+ Record is read into table->record[0]
+
+ RETURN
+ 0 Found row
+ HA_ERR_END_OF_FILE No rows in range
+ # Error code
+*/
+
+int handler::read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range_arg, bool sorted)
+{
+ int result;
+ DBUG_ENTER("handler::read_range_first");
+
+ eq_range= eq_range_arg;
+ end_range= 0;
+ if (end_key)
+ {
+ end_range= &save_end_range;
+ save_end_range= *end_key;
+ key_compare_result_on_equal= ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
+ (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
+ }
+ range_key_part= table->key_info[active_index].key_part;
+
+ if (!start_key) // Read first record
+ result= index_first(table->record[0]);
+ else
+ result= index_read(table->record[0],
+ start_key->key,
+ start_key->length,
+ start_key->flag);
+ if (result)
+ DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND ||
+ result == HA_ERR_END_OF_FILE) ? HA_ERR_END_OF_FILE :
+ result);
+
+ DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
+}
+
+
+/*
+ Read next row between two ranges.
+
+ SYNOPSIS
+ read_range_next()
+
+ NOTES
+ Record is read into table->record[0]
+
+ RETURN
+ 0 Found row
+ HA_ERR_END_OF_FILE No rows in range
+ # Error code
+*/
+
+int handler::read_range_next()
+{
+ int result;
+ DBUG_ENTER("handler::read_range_next");
+
+ if (eq_range)
+ {
+ /* We trust that index_next_same always gives a row in range */
+ DBUG_RETURN(index_next_same(table->record[0],
+ end_range->key,
+ end_range->length));
+ }
+ result= index_next(table->record[0]);
+ if (result)
+ DBUG_RETURN(result);
+ DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
+}
+
+
+/*
+ Compare if found key (in row) is over max-value
+
+ SYNOPSIS
+ compare_key
+ range range to compare to row. May be 0 for no range
+
+ NOTES
+ See key.cc::key_cmp() for details
+
+ RETURN
+ The return value is SIGN(key_in_row - range_key):
+
+ 0 Key is equal to range or 'range' == 0 (no range)
+ -1 Key is less than range
+ 1 Key is larger than range
+*/
+
+int handler::compare_key(key_range *range)
+{
+ int cmp;
+ if (!range)
+ return 0; // No max range
+ cmp= key_cmp(range_key_part, range->key, range->length);
+ if (!cmp)
+ cmp= key_compare_result_on_equal;
+ return cmp;
+}
+
+int handler::index_read_idx(byte * buf, uint index, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ int error= ha_index_init(index);
+ if (!error)
+ error= index_read(buf, key, key_len, find_flag);
+ if (!error)
+ error= ha_index_end();
+ return error;
+}
+