summaryrefslogtreecommitdiff
path: root/sql/handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/handler.cc')
-rw-r--r--sql/handler.cc2423
1 files changed, 1764 insertions, 659 deletions
diff --git a/sql/handler.cc b/sql/handler.cc
index c836c949f3a..e0955132998 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -21,147 +21,49 @@
#endif
#include "mysql_priv.h"
-#include "ha_heap.h"
-#include "ha_myisam.h"
-#include "ha_myisammrg.h"
+#include "rpl_filter.h"
-/*
- We have dummy hanldertons in case the handler has not been compiled
- in. This will be removed in 5.1.
-*/
-#ifdef HAVE_BERKELEY_DB
-#include "ha_berkeley.h"
-extern handlerton berkeley_hton;
-#else
-handlerton berkeley_hton = { "BerkeleyDB", SHOW_OPTION_NO,
- "Supports transactions and page-level locking", DB_TYPE_BERKELEY_DB, NULL,
- 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_BLACKHOLE_DB
-#include "ha_blackhole.h"
-extern handlerton blackhole_hton;
-#else
-handlerton blackhole_hton = { "BLACKHOLE", SHOW_OPTION_NO,
- "/dev/null storage engine (anything you write to it disappears)",
- DB_TYPE_BLACKHOLE_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_EXAMPLE_DB
-#include "examples/ha_example.h"
-extern handlerton example_hton;
-#else
-handlerton example_hton = { "EXAMPLE", SHOW_OPTION_NO,
- "Example storage engine",
- DB_TYPE_EXAMPLE_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#if defined(HAVE_ARCHIVE_DB)
-#include "ha_archive.h"
-extern handlerton archive_hton;
-#else
-handlerton archive_hton = { "ARCHIVE", SHOW_OPTION_NO,
- "Archive storage engine", DB_TYPE_ARCHIVE_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_CSV_DB
-#include "examples/ha_tina.h"
-extern handlerton tina_hton;
-#else
-handlerton tina_hton = { "CSV", SHOW_OPTION_NO, "CSV storage engine",
- DB_TYPE_CSV_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_INNOBASE_DB
-#include "ha_innodb.h"
-extern handlerton innobase_hton;
-#else
-handlerton innobase_hton = { "InnoDB", SHOW_OPTION_NO,
- "Supports transactions, row-level locking, and foreign keys",
- DB_TYPE_INNODB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
-#include "ha_ndbcluster.h"
-extern handlerton ndbcluster_hton;
-#else
-handlerton ndbcluster_hton = { "ndbcluster", SHOW_OPTION_NO,
- "Clustered, fault-tolerant, memory-based tables",
- DB_TYPE_NDBCLUSTER, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_FEDERATED_DB
-#include "ha_federated.h"
-extern handlerton federated_hton;
-#else
-handlerton federated_hton = { "FEDERATED", SHOW_OPTION_NO,
- "Federated MySQL storage engine", DB_TYPE_FEDERATED_DB, NULL, 0, 0, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
#include <myisampack.h>
#include <errno.h>
-extern handlerton myisam_hton;
-extern handlerton myisammrg_hton;
-extern handlerton heap_hton;
-extern handlerton binlog_hton;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h"
+#endif
/*
- Obsolete
+ While we have legacy_db_type, we have this array to
+ check for dups and to find handlerton from legacy_db_type.
+ Remove when legacy_db_type is finally gone
*/
-handlerton isam_hton = { "ISAM", SHOW_OPTION_NO, "Obsolete storage engine",
- DB_TYPE_ISAM, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, HTON_NO_FLAGS };
+st_plugin_int *hton2plugin[MAX_HA];
+
+static handlerton *installed_htons[128];
+
+#define BITMAP_STACKBUF_SIZE (128/8)
+KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} };
/* static functions defined in this file */
+static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root);
+
static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES;
/* number of entries in handlertons[] */
-ulong total_ha;
+ulong total_ha= 0;
/* number of storage engines (from handlertons[]) that support 2pc */
-ulong total_ha_2pc;
+ulong total_ha_2pc= 0;
/* size of savepoint storage area (see ha_init) */
-ulong savepoint_alloc_size;
-
-/*
- This array is used for processing compiled in engines.
-*/
-handlerton *sys_table_types[]=
-{
- &myisam_hton,
- &heap_hton,
- &innobase_hton,
- &berkeley_hton,
- &blackhole_hton,
- &example_hton,
- &archive_hton,
- &tina_hton,
- &ndbcluster_hton,
- &federated_hton,
- &myisammrg_hton,
- &binlog_hton,
- &isam_hton,
- NULL
-};
+ulong savepoint_alloc_size= 0;
-struct show_table_alias_st sys_table_aliases[]=
+static const LEX_STRING sys_table_aliases[]=
{
- {"INNOBASE", "InnoDB"},
- {"NDB", "NDBCLUSTER"},
- {"BDB", "BERKELEYDB"},
- {"HEAP", "MEMORY"},
- {"MERGE", "MRG_MYISAM"},
- {NullS, NullS}
+ { C_STRING_WITH_LEN("INNOBASE") }, { C_STRING_WITH_LEN("INNODB") },
+ { C_STRING_WITH_LEN("NDB") }, { C_STRING_WITH_LEN("NDBCLUSTER") },
+ { C_STRING_WITH_LEN("HEAP") }, { C_STRING_WITH_LEN("MEMORY") },
+ { C_STRING_WITH_LEN("MERGE") }, { C_STRING_WITH_LEN("MRG_MYISAM") },
+ {NullS, 0}
};
const char *ha_row_type[] = {
@@ -177,90 +79,123 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
uint known_extensions_id= 0;
-enum db_type ha_resolve_by_name(const char *name, uint namelen)
+
+/*
+ Return the default storage engine handlerton for thread
+
+ SYNOPSIS
+ ha_default_handlerton(thd)
+ thd current thread
+
+ RETURN
+ pointer to handlerton
+*/
+
+handlerton *ha_default_handlerton(THD *thd)
{
- THD *thd= current_thd;
- show_table_alias_st *table_alias;
- handlerton **types;
+ return (thd->variables.table_type != NULL) ?
+ thd->variables.table_type :
+ (global_system_variables.table_type != NULL ?
+ global_system_variables.table_type : myisam_hton);
+}
- if (thd && !my_strnncoll(&my_charset_latin1,
- (const uchar *)name, namelen,
- (const uchar *)"DEFAULT", 7))
- return (enum db_type) thd->variables.table_type;
-retest:
- for (types= sys_table_types; *types; types++)
+/*
+ Return the storage engine handlerton for the supplied name
+
+ SYNOPSIS
+ ha_resolve_by_name(thd, name)
+ thd current thread
+ name name of storage engine
+
+ RETURN
+ pointer to handlerton
+*/
+
+handlerton *ha_resolve_by_name(THD *thd, const LEX_STRING *name)
+{
+ const LEX_STRING *table_alias;
+ st_plugin_int *plugin;
+
+redo:
+ /* my_strnncoll is a macro and gcc doesn't do early expansion of macro */
+ if (thd && !my_charset_latin1.coll->strnncoll(&my_charset_latin1,
+ (const uchar *)name->str, name->length,
+ (const uchar *)STRING_WITH_LEN("DEFAULT"), 0))
+ return ha_default_handlerton(thd);
+
+ if ((plugin= plugin_lock(name, MYSQL_STORAGE_ENGINE_PLUGIN)))
{
- if (!my_strnncoll(&my_charset_latin1,
- (const uchar *)name, namelen,
- (const uchar *)(*types)->name, strlen((*types)->name)))
- return (enum db_type) (*types)->db_type;
+ handlerton *hton= (handlerton *)plugin->data;
+ if (!(hton->flags & HTON_NOT_USER_SELECTABLE))
+ return hton;
+ plugin_unlock(plugin);
}
/*
We check for the historical aliases.
*/
- for (table_alias= sys_table_aliases; table_alias->type; table_alias++)
+ for (table_alias= sys_table_aliases; table_alias->str; table_alias+= 2)
{
if (!my_strnncoll(&my_charset_latin1,
- (const uchar *)name, namelen,
- (const uchar *)table_alias->alias,
- strlen(table_alias->alias)))
+ (const uchar *)name->str, name->length,
+ (const uchar *)table_alias->str, table_alias->length))
{
- name= table_alias->type;
- namelen= strlen(name);
- goto retest;
+ name= table_alias + 1;
+ goto redo;
}
}
- return DB_TYPE_UNKNOWN;
+ return NULL;
}
-const char *ha_get_storage_engine(enum db_type db_type)
+const char *ha_get_storage_engine(enum legacy_db_type db_type)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- {
- if (db_type == (*types)->db_type)
- return (*types)->name;
+ switch (db_type) {
+ case DB_TYPE_DEFAULT:
+ return "DEFAULT";
+ default:
+ if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT &&
+ installed_htons[db_type])
+ return hton2plugin[installed_htons[db_type]->slot]->name.str;
+ /* fall through */
+ case DB_TYPE_UNKNOWN:
+ return "UNKNOWN";
}
- return "*NONE*";
}
-bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag)
+static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- {
- if (db_type == (*types)->db_type)
- return test((*types)->flags & flag);
- }
- return FALSE; // No matching engine
+ handlerton *hton= ha_default_handlerton(current_thd);
+ return (hton && hton->create) ? hton->create(hton, table, mem_root) : NULL;
}
-my_bool ha_storage_engine_is_enabled(enum db_type database_type)
+handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- {
- if ((database_type == (*types)->db_type) &&
- ((*types)->state == SHOW_OPTION_YES))
- return TRUE;
+ switch (db_type) {
+ case DB_TYPE_DEFAULT:
+ return ha_default_handlerton(thd);
+ case DB_TYPE_UNKNOWN:
+ return NULL;
+ default:
+ if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT)
+ return installed_htons[db_type];
+ return NULL;
}
- return FALSE;
}
/* Use other database handler if databasehandler is not compiled in */
-enum db_type ha_checktype(THD *thd, enum db_type database_type,
+handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
bool no_substitute, bool report_error)
{
- if (ha_storage_engine_is_enabled(database_type))
- return database_type;
+ handlerton *hton= ha_resolve_by_legacy_type(thd, database_type);
+ if (ha_storage_engine_is_enabled(hton))
+ return hton;
if (no_substitute)
{
@@ -269,103 +204,70 @@ enum db_type ha_checktype(THD *thd, enum db_type database_type,
const char *engine_name= ha_get_storage_engine(database_type);
my_error(ER_FEATURE_DISABLED,MYF(0),engine_name,engine_name);
}
- return DB_TYPE_UNKNOWN;
+ return NULL;
}
switch (database_type) {
#ifndef NO_HASH
case DB_TYPE_HASH:
- return (database_type);
+ return ha_resolve_by_legacy_type(thd, DB_TYPE_HASH);
#endif
case DB_TYPE_MRG_ISAM:
- return (DB_TYPE_MRG_MYISAM);
+ return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM);
default:
break;
}
- return ((enum db_type) thd->variables.table_type != DB_TYPE_UNKNOWN ?
- (enum db_type) thd->variables.table_type :
- ((enum db_type) global_system_variables.table_type !=
- DB_TYPE_UNKNOWN ?
- (enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM)
- );
+ return ha_default_handlerton(thd);
} /* ha_checktype */
-handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type)
+handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
+ handlerton *db_type)
{
- switch (db_type) {
-#ifndef NO_HASH
- case DB_TYPE_HASH:
- return new (alloc) ha_hash(table);
-#endif
- case DB_TYPE_MRG_MYISAM:
- case DB_TYPE_MRG_ISAM:
- if (have_merge_db == SHOW_OPTION_YES)
- return new (alloc) ha_myisammrg(table);
- return NULL;
-#ifdef HAVE_BERKELEY_DB
- case DB_TYPE_BERKELEY_DB:
- if (have_berkeley_db == SHOW_OPTION_YES)
- return new (alloc) ha_berkeley(table);
- return NULL;
-#endif
-#ifdef HAVE_INNOBASE_DB
- case DB_TYPE_INNODB:
- if (have_innodb == SHOW_OPTION_YES)
- return new (alloc) ha_innobase(table);
- return NULL;
-#endif
-#ifdef HAVE_EXAMPLE_DB
- case DB_TYPE_EXAMPLE_DB:
- if (have_example_db == SHOW_OPTION_YES)
- return new (alloc) ha_example(table);
- return NULL;
-#endif
-#if defined(HAVE_ARCHIVE_DB)
- case DB_TYPE_ARCHIVE_DB:
- if (have_archive_db == SHOW_OPTION_YES)
- return new (alloc) ha_archive(table);
- return NULL;
-#endif
-#ifdef HAVE_BLACKHOLE_DB
- case DB_TYPE_BLACKHOLE_DB:
- if (have_blackhole_db == SHOW_OPTION_YES)
- return new (alloc) ha_blackhole(table);
- return NULL;
-#endif
-#ifdef HAVE_FEDERATED_DB
- case DB_TYPE_FEDERATED_DB:
- if (have_federated_db == SHOW_OPTION_YES)
- return new (alloc) ha_federated(table);
- return NULL;
-#endif
-#ifdef HAVE_CSV_DB
- case DB_TYPE_CSV_DB:
- if (have_csv_db == SHOW_OPTION_YES)
- return new (alloc) ha_tina(table);
- return NULL;
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- case DB_TYPE_NDBCLUSTER:
- if (have_ndbcluster == SHOW_OPTION_YES)
- return new (alloc) ha_ndbcluster(table);
- return NULL;
-#endif
- case DB_TYPE_HEAP:
- return new (alloc) ha_heap(table);
- default: // should never happen
+ handler *file;
+ DBUG_ENTER("get_new_handler");
+ DBUG_PRINT("enter", ("alloc: 0x%lx", (long) alloc));
+
+ if (db_type && db_type->state == SHOW_OPTION_YES && db_type->create)
{
- enum db_type def=(enum db_type) current_thd->variables.table_type;
- /* Try first with 'default table type' */
- if (db_type != def)
- return get_new_handler(table, alloc, def);
+ if ((file= db_type->create(db_type, share, alloc)))
+ file->init();
+ DBUG_RETURN(file);
}
- /* Fall back to MyISAM */
- case DB_TYPE_MYISAM:
- return new (alloc) ha_myisam(table);
+ /*
+ Try the default table type
+ Here the call to current_thd() is ok as we call this function a lot of
+ times but we enter this branch very seldom.
+ */
+ DBUG_RETURN(get_new_handler(share, alloc,
+ current_thd->variables.table_type));
+}
+
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+handler *get_ha_partition(partition_info *part_info)
+{
+ ha_partition *partition;
+ DBUG_ENTER("get_ha_partition");
+ if ((partition= new ha_partition(partition_hton, part_info)))
+ {
+ if (partition->initialise_partition(current_thd->mem_root))
+ {
+ delete partition;
+ partition= 0;
+ }
+ else
+ partition->init();
+ }
+ else
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(ha_partition));
}
+ DBUG_RETURN(((handler*) partition));
}
+#endif
+
/*
Register handler error messages for use with my_error().
@@ -426,6 +328,7 @@ static int ha_init_errors(void)
SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR));
SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine");
SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED));
+ SETMSG(HA_ERR_FOREIGN_DUPLICATE_KEY, "FK constraint would lead to duplicate key");
SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER(ER_TABLE_NEEDS_UPGRADE));
SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY));
SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED));
@@ -459,35 +362,141 @@ static int ha_finish_errors(void)
}
-static inline void ha_was_inited_ok(handlerton **ht)
+int ha_finalize_handlerton(st_plugin_int *plugin)
{
- uint tmp= (*ht)->savepoint_offset;
- (*ht)->savepoint_offset= savepoint_alloc_size;
- savepoint_alloc_size+= tmp;
- (*ht)->slot= total_ha++;
- if ((*ht)->prepare)
- total_ha_2pc++;
+ handlerton *hton= (handlerton *)plugin->data;
+ DBUG_ENTER("ha_finalize_handlerton");
+
+ switch (hton->state)
+ {
+ case SHOW_OPTION_NO:
+ case SHOW_OPTION_DISABLED:
+ break;
+ case SHOW_OPTION_YES:
+ if (installed_htons[hton->db_type] == hton)
+ installed_htons[hton->db_type]= NULL;
+ break;
+ };
+
+ if (hton->panic)
+ hton->panic(hton, HA_PANIC_CLOSE);
+
+ if (plugin->plugin->deinit)
+ {
+ /*
+ Today we have no defined/special behavior for uninstalling
+ engine plugins.
+ */
+ DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
+ if (plugin->plugin->deinit(NULL))
+ {
+ DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
+ plugin->name.str));
+ }
+ }
+
+ my_free((gptr)hton, MYF(0));
+
+ DBUG_RETURN(0);
}
-int ha_init()
+
+int ha_initialize_handlerton(st_plugin_int *plugin)
{
- int error= 0;
- handlerton **types;
- total_ha= savepoint_alloc_size= 0;
+ handlerton *hton;
+ DBUG_ENTER("ha_initialize_handlerton");
- if (ha_init_errors())
- return 1;
+ hton= (handlerton *)my_malloc(sizeof(handlerton),
+ MYF(MY_WME | MY_ZEROFILL));
+ /* Historical Requirement */
+ plugin->data= hton; // shortcut for the future
+ if (plugin->plugin->init)
+ {
+ if (plugin->plugin->init(hton))
+ {
+ sql_print_error("Plugin '%s' init function returned error.",
+ plugin->name.str);
+ goto err;
+ }
+ }
/*
- We now initialize everything here.
+ the switch below and hton->state should be removed when
+ command-line options for plugins will be implemented
*/
- for (types= sys_table_types; *types; types++)
- {
- if (!(*types)->init || !(*types)->init())
- ha_was_inited_ok(types);
- else
- (*types)->state= SHOW_OPTION_DISABLED;
+ switch (hton->state) {
+ case SHOW_OPTION_NO:
+ break;
+ case SHOW_OPTION_YES:
+ {
+ uint tmp;
+ /* now check the db_type for conflict */
+ if (hton->db_type <= DB_TYPE_UNKNOWN ||
+ hton->db_type >= DB_TYPE_DEFAULT ||
+ installed_htons[hton->db_type])
+ {
+ int idx= (int) DB_TYPE_FIRST_DYNAMIC;
+
+ while (idx < (int) DB_TYPE_DEFAULT && installed_htons[idx])
+ idx++;
+
+ if (idx == (int) DB_TYPE_DEFAULT)
+ {
+ sql_print_warning("Too many storage engines!");
+ DBUG_RETURN(1);
+ }
+ if (hton->db_type != DB_TYPE_UNKNOWN)
+ sql_print_warning("Storage engine '%s' has conflicting typecode. "
+ "Assigning value %d.", plugin->plugin->name, idx);
+ hton->db_type= (enum legacy_db_type) idx;
+ }
+ installed_htons[hton->db_type]= hton;
+ tmp= hton->savepoint_offset;
+ hton->savepoint_offset= savepoint_alloc_size;
+ savepoint_alloc_size+= tmp;
+ hton->slot= total_ha++;
+ hton2plugin[hton->slot]=plugin;
+ if (hton->prepare)
+ total_ha_2pc++;
+ break;
+ }
+ /* fall through */
+ default:
+ hton->state= SHOW_OPTION_DISABLED;
+ break;
}
+
+ /*
+ This is entirely for legacy. We will create a new "disk based" hton and a
+ "memory" hton which will be configurable longterm. We should be able to
+ remove partition and myisammrg.
+ */
+ switch (hton->db_type) {
+ case DB_TYPE_HEAP:
+ heap_hton= hton;
+ break;
+ case DB_TYPE_MYISAM:
+ myisam_hton= hton;
+ break;
+ case DB_TYPE_PARTITION_DB:
+ partition_hton= hton;
+ break;
+ default:
+ break;
+ };
+
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+int ha_init()
+{
+ int error= 0;
+ DBUG_ENTER("ha_init");
+
+ if (ha_init_errors())
+ DBUG_RETURN(1);
DBUG_ASSERT(total_ha < MAX_HA);
/*
@@ -497,73 +506,61 @@ int ha_init()
*/
opt_using_transactions= total_ha>(ulong)opt_bin_log;
savepoint_alloc_size+= sizeof(SAVEPOINT);
- return error;
+ DBUG_RETURN(error);
}
- /* close, flush or restart databases */
- /* Ignore this for other databases than ours */
-
-int ha_panic(enum ha_panic_function flag)
+int ha_end()
{
- int error=0;
-#ifndef NO_HASH
- error|=h_panic(flag); /* fix hash */
-#endif
-#ifdef HAVE_ISAM
- error|=mrg_panic(flag);
- error|=nisam_panic(flag);
-#endif
- error|=heap_panic(flag);
- error|=mi_panic(flag);
- error|=myrg_panic(flag);
-#ifdef HAVE_BERKELEY_DB
- if (have_berkeley_db == SHOW_OPTION_YES)
- error|=berkeley_end();
-#endif
-#ifdef HAVE_INNOBASE_DB
- if (have_innodb == SHOW_OPTION_YES)
- error|=innobase_end();
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error|=ndbcluster_end();
-#endif
-#ifdef HAVE_FEDERATED_DB
- if (have_federated_db == SHOW_OPTION_YES)
- error|= federated_db_end();
-#endif
-#if defined(HAVE_ARCHIVE_DB)
- if (have_archive_db == SHOW_OPTION_YES)
- error|= archive_db_end();
-#endif
-#ifdef HAVE_CSV_DB
- if (have_csv_db == SHOW_OPTION_YES)
- error|= tina_end();
-#endif
+ int error= 0;
+ DBUG_ENTER("ha_end");
+
+
+ /*
+ This should be eventualy based on the graceful shutdown flag.
+ So if flag is equal to HA_PANIC_CLOSE, the deallocate
+ the errors.
+ */
if (ha_finish_errors())
error= 1;
- return error;
-} /* ha_panic */
+
+ DBUG_RETURN(error);
+}
+
+static my_bool dropdb_handlerton(THD *unused1, st_plugin_int *plugin,
+ void *path)
+{
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->drop_database)
+ hton->drop_database(hton, (char *)path);
+ return FALSE;
+}
+
void ha_drop_database(char* path)
{
-#ifdef HAVE_INNOBASE_DB
- 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
+ plugin_foreach(NULL, dropdb_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, path);
+}
+
+
+static my_bool closecon_handlerton(THD *thd, st_plugin_int *plugin,
+ void *unused)
+{
+ handlerton *hton= (handlerton *)plugin->data;
+ /*
+ there's no need to rollback here as all transactions must
+ be rolled back already
+ */
+ if (hton->state == SHOW_OPTION_YES && hton->close_connection &&
+ thd->ha_data[hton->slot])
+ hton->close_connection(hton, thd);
+ return FALSE;
}
+
/* don't bother to rollback here, it's done already */
void ha_close_connection(THD* thd)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- if (thd->ha_data[(*types)->slot])
- (*types)->close_connection(thd);
+ plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
}
/* ========================================================================
@@ -631,7 +628,7 @@ int ha_prepare(THD *thd)
statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status);
if ((*ht)->prepare)
{
- if ((err= (*(*ht)->prepare)(thd, all)))
+ if ((err= (*(*ht)->prepare)(*ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
ha_rollback_trans(thd, all);
@@ -642,7 +639,8 @@ int ha_prepare(THD *thd)
else
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), (*ht)->name);
+ ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ hton2plugin[(*ht)->slot]->name.str);
}
}
}
@@ -704,7 +702,7 @@ int ha_commit_trans(THD *thd, bool all)
for (; *ht && !error; ht++)
{
int err;
- if ((err= (*(*ht)->prepare)(thd, all)))
+ if ((err= (*(*ht)->prepare)(*ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error= 1;
@@ -751,7 +749,7 @@ int ha_commit_one_phase(THD *thd, bool all)
for (ht=trans->ht; *ht; ht++)
{
int err;
- if ((err= (*(*ht)->commit)(thd, all)))
+ if ((err= (*(*ht)->commit)(*ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error=1;
@@ -807,7 +805,7 @@ int ha_rollback_trans(THD *thd, bool all)
for (handlerton **ht=trans->ht; *ht; ht++)
{
int err;
- if ((err= (*(*ht)->rollback)(thd, all)))
+ if ((err= (*(*ht)->rollback)(*ht, thd, all)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
@@ -873,21 +871,46 @@ int ha_autocommit_or_rollback(THD *thd, int error)
}
-int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
+struct xahton_st {
+ XID *xid;
+ int result;
+};
+
+static my_bool xacommit_handlerton(THD *unused1, st_plugin_int *plugin,
+ void *arg)
{
- handlerton **types;
- int res= 1;
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->recover)
+ {
+ hton->commit_by_xid(hton, ((struct xahton_st *)arg)->xid);
+ ((struct xahton_st *)arg)->result= 0;
+ }
+ return FALSE;
+}
- for (types= sys_table_types; *types; types++)
+static my_bool xarollback_handlerton(THD *unused1, st_plugin_int *plugin,
+ void *arg)
+{
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->recover)
{
- if ((*types)->state == SHOW_OPTION_YES && (*types)->recover)
- {
- if ((*(commit ? (*types)->commit_by_xid :
- (*types)->rollback_by_xid))(xid))
- res= 0;
- }
+ hton->rollback_by_xid(hton, ((struct xahton_st *)arg)->xid);
+ ((struct xahton_st *)arg)->result= 0;
}
- return res;
+ return FALSE;
+}
+
+
+int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
+{
+ struct xahton_st xaop;
+ xaop.xid= xid;
+ xaop.result= 1;
+
+ plugin_foreach(NULL, commit ? xacommit_handlerton : xarollback_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &xaop);
+
+ return xaop.result;
}
@@ -963,99 +986,123 @@ static char* xid_to_str(char *buf, XID *xid)
in this case commit_list==0, tc_heuristic_recover == 0
there should be no prepared transactions in this case.
*/
-int ha_recover(HASH *commit_list)
-{
- int len, got, found_foreign_xids=0, found_my_xids=0;
- handlerton **types;
- XID *list=0;
- bool dry_run=(commit_list==0 && tc_heuristic_recover==0);
- DBUG_ENTER("ha_recover");
-
- /* commit_list and tc_heuristic_recover cannot be set both */
- DBUG_ASSERT(commit_list==0 || tc_heuristic_recover==0);
- /* if either is set, total_ha_2pc must be set too */
- DBUG_ASSERT(dry_run || total_ha_2pc>(ulong)opt_bin_log);
-
- if (total_ha_2pc <= (ulong)opt_bin_log)
- DBUG_RETURN(0);
-
- if (commit_list)
- sql_print_information("Starting crash recovery...");
-#ifndef WILL_BE_DELETED_LATER
- /*
- for now, only InnoDB supports 2pc. It means we can always safely
- rollback all pending transactions, without risking inconsistent data
- */
- DBUG_ASSERT(total_ha_2pc == (ulong) opt_bin_log+1); // only InnoDB and binlog
- tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
- dry_run=FALSE;
-#endif
+struct xarecover_st
+{
+ int len, found_foreign_xids, found_my_xids;
+ XID *list;
+ HASH *commit_list;
+ bool dry_run;
+};
- for (len= MAX_XID_LIST_SIZE ; list==0 && len > MIN_XID_LIST_SIZE; len/=2)
- {
- list=(XID *)my_malloc(len*sizeof(XID), MYF(0));
- }
- if (!list)
- {
- sql_print_error(ER(ER_OUTOFMEMORY), len*sizeof(XID));
- DBUG_RETURN(1);
- }
+static my_bool xarecover_handlerton(THD *unused, st_plugin_int *plugin,
+ void *arg)
+{
+ handlerton *hton= (handlerton *)plugin->data;
+ struct xarecover_st *info= (struct xarecover_st *) arg;
+ int got;
- for (types= sys_table_types; *types; types++)
+ if (hton->state == SHOW_OPTION_YES && hton->recover)
{
- if ((*types)->state != SHOW_OPTION_YES || !(*types)->recover)
- continue;
- while ((got=(*(*types)->recover)(list, len)) > 0 )
+ while ((got= hton->recover(hton, info->list, info->len)) > 0 )
{
sql_print_information("Found %d prepared transaction(s) in %s",
- got, (*types)->name);
+ got, hton2plugin[hton->slot]->name.str);
for (int i=0; i < got; i ++)
{
- my_xid x=list[i].get_my_xid();
+ my_xid x=info->list[i].get_my_xid();
if (!x) // not "mine" - that is generated by external TM
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("ignore xid %s", xid_to_str(buf, list+i));
+ sql_print_information("ignore xid %s", xid_to_str(buf, info->list+i));
#endif
- xid_cache_insert(list+i, XA_PREPARED);
- found_foreign_xids++;
+ xid_cache_insert(info->list+i, XA_PREPARED);
+ info->found_foreign_xids++;
continue;
}
- if (dry_run)
+ if (info->dry_run)
{
- found_my_xids++;
+ info->found_my_xids++;
continue;
}
// recovery mode
- if (commit_list ?
- hash_search(commit_list, (byte *)&x, sizeof(x)) != 0 :
+ if (info->commit_list ?
+ hash_search(info->commit_list, (byte *)&x, sizeof(x)) != 0 :
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("commit xid %s", xid_to_str(buf, list+i));
+ sql_print_information("commit xid %s", xid_to_str(buf, info->list+i));
#endif
- (*(*types)->commit_by_xid)(list+i);
+ hton->commit_by_xid(hton, info->list+i);
}
else
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("rollback xid %s", xid_to_str(buf, list+i));
+ sql_print_information("rollback xid %s",
+ xid_to_str(buf, info->list+i));
#endif
- (*(*types)->rollback_by_xid)(list+i);
+ hton->rollback_by_xid(hton, info->list+i);
}
}
- if (got < len)
+ if (got < info->len)
break;
}
}
- my_free((gptr)list, MYF(0));
- if (found_foreign_xids)
- sql_print_warning("Found %d prepared XA transactions", found_foreign_xids);
- if (dry_run && found_my_xids)
+ return FALSE;
+}
+
+int ha_recover(HASH *commit_list)
+{
+ struct xarecover_st info;
+ DBUG_ENTER("ha_recover");
+ info.found_foreign_xids= info.found_my_xids= 0;
+ info.commit_list= commit_list;
+ info.dry_run= (info.commit_list==0 && tc_heuristic_recover==0);
+ info.list= NULL;
+
+ /* commit_list and tc_heuristic_recover cannot be set both */
+ DBUG_ASSERT(info.commit_list==0 || tc_heuristic_recover==0);
+ /* if either is set, total_ha_2pc must be set too */
+ DBUG_ASSERT(info.dry_run || total_ha_2pc>(ulong)opt_bin_log);
+
+ if (total_ha_2pc <= (ulong)opt_bin_log)
+ DBUG_RETURN(0);
+
+ if (info.commit_list)
+ sql_print_information("Starting crash recovery...");
+
+#ifndef WILL_BE_DELETED_LATER
+ /*
+ for now, only InnoDB supports 2pc. It means we can always safely
+ rollback all pending transactions, without risking inconsistent data
+ */
+ DBUG_ASSERT(total_ha_2pc == (ulong) opt_bin_log+1); // only InnoDB and binlog
+ tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
+ info.dry_run=FALSE;
+#endif
+
+ for (info.len= MAX_XID_LIST_SIZE ;
+ info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2)
+ {
+ info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0));
+ }
+ if (!info.list)
+ {
+ sql_print_error(ER(ER_OUTOFMEMORY), info.len*sizeof(XID));
+ DBUG_RETURN(1);
+ }
+
+ plugin_foreach(NULL, xarecover_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &info);
+
+ my_free((gptr)info.list, MYF(0));
+ if (info.found_foreign_xids)
+ sql_print_warning("Found %d prepared XA transactions",
+ info.found_foreign_xids);
+ if (info.dry_run && info.found_my_xids)
{
sql_print_error("Found %d prepared transactions! It means that mysqld was "
"not shut down properly last time and critical recovery "
@@ -1063,10 +1110,10 @@ int ha_recover(HASH *commit_list)
"after a crash. You have to start mysqld with "
"--tc-heuristic-recover switch to commit or rollback "
"pending transactions.",
- found_my_xids, opt_tc_log_file);
+ info.found_my_xids, opt_tc_log_file);
DBUG_RETURN(1);
}
- if (commit_list)
+ if (info.commit_list)
sql_print_information("Crash recovery finished.");
DBUG_RETURN(0);
}
@@ -1137,27 +1184,23 @@ bool mysql_xa_recover(THD *thd)
return value: always 0
*/
-int ha_release_temporary_latches(THD *thd)
+static my_bool release_temporary_latches(THD *thd, st_plugin_int *plugin,
+ void *unused)
{
-#ifdef HAVE_INNOBASE_DB
- if (opt_innodb)
- innobase_release_temporary_latches(thd);
-#endif
- return 0;
-}
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->release_temporary_latches)
+ hton->release_temporary_latches(hton, thd);
+
+ return FALSE;
+}
-/*
- Export statistics for different engines. Currently we use it only for
- InnoDB.
-*/
-int ha_update_statistics()
+int ha_release_temporary_latches(THD *thd)
{
-#ifdef HAVE_INNOBASE_DB
- if (opt_innodb)
- innodb_export_status();
-#endif
+ plugin_foreach(thd, release_temporary_latches, MYSQL_STORAGE_ENGINE_PLUGIN,
+ NULL);
+
return 0;
}
@@ -1180,12 +1223,13 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
{
int err;
DBUG_ASSERT((*ht)->savepoint_set != 0);
- if ((err= (*(*ht)->savepoint_rollback)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
+ if ((err= (*(*ht)->savepoint_rollback)(*ht, thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
- statistic_increment(thd->status_var.ha_savepoint_rollback_count,&LOCK_status);
+ statistic_increment(thd->status_var.ha_savepoint_rollback_count,
+ &LOCK_status);
trans->no_2pc|=(*ht)->prepare == 0;
}
/*
@@ -1195,7 +1239,7 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
for (; *ht ; ht++)
{
int err;
- if ((err= (*(*ht)->rollback)(thd, !thd->in_sub_stmt)))
+ if ((err= (*(*ht)->rollback)(*ht, thd, !thd->in_sub_stmt)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
@@ -1229,7 +1273,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
error=1;
break;
}
- if ((err= (*(*ht)->savepoint_set)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
+ if ((err= (*(*ht)->savepoint_set)(*ht, thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
{ // cannot happen
my_error(ER_GET_ERRNO, MYF(0), err);
error=1;
@@ -1255,7 +1299,9 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
int err;
if (!(*ht)->savepoint_release)
continue;
- if ((err= (*(*ht)->savepoint_release)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
+ if ((err= (*(*ht)->savepoint_release)(*ht, thd,
+ (byte *)(sv+1)+
+ (*ht)->savepoint_offset)))
{ // cannot happen
my_error(ER_GET_ERRNO, MYF(0), err);
error=1;
@@ -1265,38 +1311,63 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
}
+static my_bool snapshot_handlerton(THD *thd, st_plugin_int *plugin,
+ void *arg)
+{
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES &&
+ hton->start_consistent_snapshot)
+ {
+ hton->start_consistent_snapshot(hton, thd);
+ *((bool *)arg)= false;
+ }
+ return FALSE;
+}
+
int ha_start_consistent_snapshot(THD *thd)
{
-#ifdef HAVE_INNOBASE_DB
- if ((have_innodb == SHOW_OPTION_YES) &&
- !innobase_start_trx_and_assign_read_view(thd))
- return 0;
-#endif
+ bool warn= true;
+
+ plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);
+
/*
Same idea as when one wants to CREATE TABLE in one engine which does not
exist:
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "This MySQL server does not support any "
- "consistent-read capable storage engine");
+ if (warn)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "This MySQL server does not support any "
+ "consistent-read capable storage engine");
return 0;
}
-bool ha_flush_logs()
+static my_bool flush_handlerton(THD *thd, st_plugin_int *plugin,
+ void *arg)
{
- bool result=0;
-#ifdef HAVE_BERKELEY_DB
- if ((have_berkeley_db == SHOW_OPTION_YES) &&
- berkeley_flush_logs())
- result=1;
-#endif
-#ifdef HAVE_INNOBASE_DB
- if ((have_innodb == SHOW_OPTION_YES) &&
- innobase_flush_logs())
- result=1;
-#endif
- return result;
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
+ hton->flush_logs(hton))
+ return TRUE;
+ return FALSE;
+}
+
+
+bool ha_flush_logs(handlerton *db_type)
+{
+ if (db_type == NULL)
+ {
+ if (plugin_foreach(NULL, flush_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, 0))
+ return TRUE;
+ }
+ else
+ {
+ if (db_type->state != SHOW_OPTION_YES ||
+ (db_type->flush_logs && db_type->flush_logs(db_type)))
+ return TRUE;
+ }
+ return FALSE;
}
/*
@@ -1304,8 +1375,8 @@ bool ha_flush_logs()
The .frm file will be deleted only if we return 0 or ENOENT
*/
-int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
- const char *alias, bool generate_warning)
+int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
+ const char *db, const char *alias, bool generate_warning)
{
handler *file;
char tmp_path[FN_REFLEN];
@@ -1319,11 +1390,11 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
dummy_table.s= &dummy_share;
/* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
- if (table_type == DB_TYPE_UNKNOWN ||
- ! (file=get_new_handler(&dummy_table, thd->mem_root, table_type)))
+ if (table_type == NULL ||
+ ! (file=get_new_handler(&dummy_share, thd->mem_root, table_type)))
DBUG_RETURN(ENOENT);
- if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
+ if (lower_case_table_names == 2 && !(file->ha_table_flags() & HA_FILE_BASED))
{
/* Ensure that table handler get path in lower case */
strmov(tmp_path, path);
@@ -1353,7 +1424,12 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
thd->net.last_error[0]= 0;
/* Fill up strucutures that print_error may need */
- dummy_table.s->path= path;
+ dummy_share.path.str= (char*) path;
+ dummy_share.path.length= strlen(path);
+ dummy_share.db.str= (char*) db;
+ dummy_share.db.length= strlen(db);
+ dummy_share.table_name.str= (char*) alias;
+ dummy_share.table_name.length= strlen(alias);
dummy_table.alias= alias;
file->print_error(error, 0);
@@ -1376,24 +1452,72 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
****************************************************************************/
handler *handler::clone(MEM_ROOT *mem_root)
{
- handler *new_handler= get_new_handler(table, mem_root, table->s->db_type);
- if (new_handler && !new_handler->ha_open(table->s->path, table->db_stat,
+ handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type);
+ if (new_handler && !new_handler->ha_open(table,
+ table->s->normalized_path.str,
+ table->db_stat,
HA_OPEN_IGNORE_IF_LOCKED))
return new_handler;
return NULL;
}
- /* 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)
+void handler::ha_statistic_increment(ulong SSV::*offset) const
+{
+ statistic_increment(table->in_use->status_var.*offset, &LOCK_status);
+}
+
+
+bool handler::check_if_log_table_locking_is_allowed(uint sql_command,
+ ulong type, TABLE *table)
+{
+ /*
+ Deny locking of the log tables, which is incompatible with
+ concurrent insert. The routine is not called if the table is
+ being locked from a logger THD (general_log_thd or slow_log_thd)
+ or from a privileged thread (see log.cc for details)
+ */
+ if (table->s->log_table &&
+ sql_command != SQLCOM_TRUNCATE &&
+ sql_command != SQLCOM_ALTER_TABLE &&
+ !(sql_command == SQLCOM_FLUSH &&
+ type & REFRESH_LOG) &&
+ (table->reginfo.lock_type >= TL_READ_NO_INSERT))
+ {
+ /*
+ The check >= TL_READ_NO_INSERT denies all write locks
+ plus the only read lock (TL_READ_NO_INSERT itself)
+ */
+ table->reginfo.lock_type == TL_READ_NO_INSERT ?
+ my_error(ER_CANT_READ_LOCK_LOG_TABLE, MYF(0)) :
+ my_error(ER_CANT_WRITE_LOCK_LOG_TABLE, MYF(0));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ Open database-handler.
+
+ IMPLEMENTATION
+ Try O_RDONLY if cannot open as O_RDWR
+ Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set
+*/
+
+int handler::ha_open(TABLE *table_arg, const char *name, int mode,
+ int test_if_locked)
{
int error;
DBUG_ENTER("handler::ha_open");
- DBUG_PRINT("enter",("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d",
- name, table->s->db_type, table->db_stat, mode,
- test_if_locked));
+ DBUG_PRINT("enter",
+ ("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d",
+ name, ht->db_type, table_arg->db_stat, mode,
+ test_if_locked));
+
+ table= table_arg;
+ DBUG_ASSERT(table->s == table_share);
+ DBUG_ASSERT(alloc_root_inited(&table->mem_root));
if ((error=open(name,mode,test_if_locked)))
{
@@ -1406,7 +1530,7 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
}
if (error)
{
- my_errno=error; /* Safeguard */
+ my_errno= error; /* Safeguard */
DBUG_PRINT("error",("error: %d errno: %d",error,errno));
}
else
@@ -1415,23 +1539,23 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
- DBUG_ASSERT(alloc_root_inited(&table->mem_root));
-
if (!(ref= (byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2)))
{
close();
error=HA_ERR_OUT_OF_MEM;
}
else
- dupp_ref=ref+ALIGN_SIZE(ref_length);
+ dup_ref=ref+ALIGN_SIZE(ref_length);
+ cached_table_flags= table_flags();
}
DBUG_RETURN(error);
}
+
/*
Read first row (only) from a table
- This is never called for InnoDB or BDB tables, as these table types
- has the HA_NOT_EXACT_COUNT set.
+ This is never called for InnoDB tables, as these table types
+ has the HA_STATS_RECORDS_IS_EXACT set.
*/
int handler::read_first_row(byte * buf, uint primary_key)
@@ -1439,14 +1563,15 @@ int handler::read_first_row(byte * buf, uint primary_key)
register int error;
DBUG_ENTER("handler::read_first_row");
- statistic_increment(current_thd->status_var.ha_read_first_count,&LOCK_status);
+ statistic_increment(table->in_use->status_var.ha_read_first_count,
+ &LOCK_status);
/*
If there is very few deleted rows in the table, find the first row by
scanning the table.
TODO remove the test for HA_READ_ORDER
*/
- if (deleted < 10 || primary_key >= MAX_KEY ||
+ if (stats.deleted < 10 || primary_key >= MAX_KEY ||
!(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
{
(void) ha_rnd_init(1);
@@ -1456,7 +1581,7 @@ int handler::read_first_row(byte * buf, uint primary_key)
else
{
/* Find the first row through the primary key */
- (void) ha_index_init(primary_key);
+ (void) ha_index_init(primary_key, 0);
error=index_first(buf);
(void) ha_index_end();
}
@@ -1464,7 +1589,10 @@ int handler::read_first_row(byte * buf, uint primary_key)
}
/*
- Generate the next auto-increment number based on increment and offset
+ Generate the next auto-increment number based on increment and offset:
+ computes the lowest number
+ - strictly greater than "nr"
+ - of the form: auto_increment_offset + N * auto_increment_increment
In most cases increment= offset= 1, in which case we get:
1,2,3,4,5,...
@@ -1473,8 +1601,10 @@ int handler::read_first_row(byte * buf, uint primary_key)
*/
inline ulonglong
-next_insert_id(ulonglong nr,struct system_variables *variables)
+compute_next_insert_id(ulonglong nr,struct system_variables *variables)
{
+ if (variables->auto_increment_increment == 1)
+ return (nr+1); // optimization of the formula below
nr= (((nr+ variables->auto_increment_increment -
variables->auto_increment_offset)) /
(ulonglong) variables->auto_increment_increment);
@@ -1490,16 +1620,8 @@ void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr)
explicitely-specified value larger than this, we need to increase
THD::next_insert_id to be greater than the explicit value.
*/
- THD *thd= table->in_use;
- if (thd->clear_next_insert_id && (nr >= thd->next_insert_id))
- {
- if (thd->variables.auto_increment_increment != 1)
- nr= next_insert_id(nr, &thd->variables);
- else
- nr++;
- thd->next_insert_id= nr;
- DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr));
- }
+ if ((next_insert_id > 0) && (nr >= next_insert_id))
+ set_next_insert_id(compute_next_insert_id(nr, &table->in_use->variables));
}
@@ -1547,7 +1669,7 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
Update the auto_increment field if necessary
SYNOPSIS
- update_auto_increment()
+ update_auto_increment()
RETURN
0 ok
@@ -1555,11 +1677,11 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
get_auto_increment() was called and returned ~(ulonglong) 0
HA_ERR_AUTOINC_ERANGE
storing value in field caused strict mode failure.
-
+
IMPLEMENTATION
- Updates columns with type NEXT_NUMBER if:
+ Updates the record's Field of type NEXT_NUMBER if:
- If column value is set to NULL (in which case
auto_increment_field_not_null is 0)
@@ -1567,24 +1689,31 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
set. In the future we will only set NEXT_NUMBER fields if one sets them
to NULL (or they are not included in the insert list).
+ In those cases, we check if the currently reserved interval still has
+ values we have not used. If yes, we pick the smallest one and use it.
+ Otherwise:
- There are two different cases when the above is true:
+ - If a list of intervals has been provided to the statement via SET
+ INSERT_ID or via an Intvar_log_event (in a replication slave), we pick the
+ first unused interval from this list, consider it as reserved.
- - thd->next_insert_id == 0 (This is the normal case)
- In this case we set the set the column for the first row to the value
- next_insert_id(get_auto_increment(column))) which is normally
- max-used-column-value +1.
+ - Otherwise we set the column for the first row to the value
+ next_insert_id(get_auto_increment(column))) which is usually
+ max-used-column-value+1.
+ We call get_auto_increment() for the first row in a multi-row
+ statement. get_auto_increment() will tell us the interval of values it
+ reserved for us.
- We call get_auto_increment() only for the first row in a multi-row
- statement. For the following rows we generate new numbers based on the
- last used number.
+ - In both cases, for the following rows we use those reserved values without
+ calling the handler again (we just progress in the interval, computing
+ each new value from the previous one). Until we have exhausted them, then
+ we either take the next provided interval or call get_auto_increment()
+ again to reserve a new interval.
- - thd->next_insert_id != 0. This happens when we have read a statement
- from the binary log or when one has used SET LAST_INSERT_ID=#.
-
- In this case we will set the column to the value of next_insert_id.
- The next row will be given the id
- next_insert_id(next_insert_id)
+ - In both cases, the reserved intervals are remembered in
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog if statement-based
+ binlogging; the last reserved interval is remembered in
+ auto_inc_interval_for_cur_row.
The idea is that generated auto_increment values are predictable and
independent of the column values in the table. This is needed to be
@@ -1595,119 +1724,254 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
inserts a column with a higher value than the last used one, we will
start counting from the inserted value.
- thd->next_insert_id is cleared after it's been used for a statement.
+ This function's "outputs" are: the table's auto_increment field is filled
+ with a value, thd->next_insert_id is filled with the value to use for the
+ next row, if a value was autogenerated for the current row it is stored in
+ thd->insert_id_for_cur_row, if get_auto_increment() was called
+ thd->auto_inc_interval_for_cur_row is modified, if that interval is not
+ present in thd->auto_inc_intervals_in_cur_stmt_for_binlog it is added to
+ this list.
+
+ TODO
+
+ Replace all references to "next number" or NEXT_NUMBER to
+ "auto_increment", everywhere (see below: there is
+ table->auto_increment_field_not_null, and there also exists
+ table->next_number_field, it's not consistent).
+
*/
+#define AUTO_INC_DEFAULT_NB_ROWS 1 // Some prefer 1024 here
+#define AUTO_INC_DEFAULT_NB_MAX_BITS 16
+#define AUTO_INC_DEFAULT_NB_MAX ((1 << AUTO_INC_DEFAULT_NB_MAX_BITS) - 1)
+
int handler::update_auto_increment()
{
- ulonglong nr;
+ ulonglong nr, nb_reserved_values;
+ bool append= FALSE;
THD *thd= table->in_use;
struct system_variables *variables= &thd->variables;
bool auto_increment_field_not_null;
DBUG_ENTER("handler::update_auto_increment");
/*
- We must save the previous value to be able to restore it if the
- row was not inserted
+ next_insert_id is a "cursor" into the reserved interval, it may go greater
+ than the interval, but not smaller.
*/
- thd->prev_insert_id= thd->next_insert_id;
+ DBUG_ASSERT(next_insert_id >= auto_inc_interval_for_cur_row.minimum());
auto_increment_field_not_null= table->auto_increment_field_not_null;
- table->auto_increment_field_not_null= FALSE;
+ table->auto_increment_field_not_null= FALSE; // to reset for next row
if ((nr= table->next_number_field->val_int()) != 0 ||
auto_increment_field_not_null &&
thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
{
- /* Clear flag for next row */
- /* Mark that we didn't generate a new value **/
- auto_increment_column_changed=0;
+ /*
+ Update next_insert_id if we had already generated a value in this
+ statement (case of INSERT VALUES(null),(3763),(null):
+ the last NULL needs to insert 3764, not the value of the first NULL plus
+ 1).
+ */
adjust_next_insert_id_after_explicit_value(nr);
+ insert_id_for_cur_row= 0; // didn't generate anything
DBUG_RETURN(0);
}
- if (!(nr= thd->next_insert_id))
- {
- if ((nr= get_auto_increment()) == ~(ulonglong) 0)
- DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure
- if (variables->auto_increment_increment != 1)
- nr= next_insert_id(nr-1, variables);
- /*
- Update next row based on the found value. This way we don't have to
- call the handler for every generated auto-increment value on a
- multi-row statement
- */
- thd->next_insert_id= nr;
+ if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum())
+ {
+ /* next_insert_id is beyond what is reserved, so we reserve more. */
+ const Discrete_interval *forced=
+ thd->auto_inc_intervals_forced.get_next();
+ if (forced != NULL)
+ {
+ nr= forced->minimum();
+ nb_reserved_values= forced->values();
+ }
+ else
+ {
+ /*
+ handler::estimation_rows_to_insert was set by
+ handler::ha_start_bulk_insert(); if 0 it means "unknown".
+ */
+ uint nb_already_reserved_intervals=
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements();
+ ulonglong nb_desired_values;
+ /*
+ If an estimation was given to the engine:
+ - use it.
+ - if we already reserved numbers, it means the estimation was
+ not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_ROWS the 2nd
+ time, twice that the 3rd time etc.
+ If no estimation was given, use those increasing defaults from the
+ start, starting from AUTO_INC_DEFAULT_NB_ROWS.
+ Don't go beyond a max to not reserve "way too much" (because
+ reservation means potentially losing unused values).
+ */
+ if (nb_already_reserved_intervals == 0 &&
+ (estimation_rows_to_insert > 0))
+ nb_desired_values= estimation_rows_to_insert;
+ else /* go with the increasing defaults */
+ {
+ /* avoid overflow in formula, with this if() */
+ if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS)
+ {
+ nb_desired_values= AUTO_INC_DEFAULT_NB_ROWS *
+ (1 << nb_already_reserved_intervals);
+ set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX);
+ }
+ else
+ nb_desired_values= AUTO_INC_DEFAULT_NB_MAX;
+ }
+ /* This call ignores all its parameters but nr, currently */
+ get_auto_increment(variables->auto_increment_offset,
+ variables->auto_increment_increment,
+ nb_desired_values, &nr,
+ &nb_reserved_values);
+ if (nr == ~(ulonglong) 0)
+ DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure
+
+ /*
+ That rounding below should not be needed when all engines actually
+ respect offset and increment in get_auto_increment(). But they don't
+ so we still do it. Wonder if for the not-first-in-index we should do
+ it. Hope that this rounding didn't push us out of the interval; even
+ if it did we cannot do anything about it (calling the engine again
+ will not help as we inserted no row).
+ */
+ nr= compute_next_insert_id(nr-1, variables);
+ }
+
+ if (table->s->next_number_key_offset == 0)
+ {
+ /* We must defer the appending until "nr" has been possibly truncated */
+ append= TRUE;
+ }
+ else
+ {
+ /*
+ For such auto_increment there is no notion of interval, just a
+ singleton. The interval is not even stored in
+ thd->auto_inc_interval_for_cur_row, so we are sure to call the engine
+ for next row.
+ */
+ DBUG_PRINT("info",("auto_increment: special not-first-in-index"));
+ }
}
DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));
- /* Mark that we should clear next_insert_id before next stmt */
- thd->clear_next_insert_id= 1;
-
- if (likely(!table->next_number_field->store((longlong) nr, TRUE)))
- thd->insert_id((ulonglong) nr);
- else
- if (thd->killed != THD::KILL_BAD_DATA) /* did we fail strict mode? */
+ if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
{
/*
- overflow of the field; we'll use the max value, however we try to
- decrease it to honour auto_increment_* variables:
+ first test if the query was aborted due to strict mode constraints
+ */
+ if (thd->killed == THD::KILL_BAD_DATA)
+ DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
+
+ /*
+ field refused this value (overflow) and truncated it, use the result of
+ the truncation (which is going to be inserted); however we try to
+ decrease it to honour auto_increment_* variables.
+ That will shift the left bound of the reserved interval, we don't
+ bother shifting the right bound (anyway any other value from this
+ interval will cause a duplicate key).
*/
nr= prev_insert_id(table->next_number_field->val_int(), variables);
- thd->insert_id(nr);
if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
- thd->insert_id(nr= table->next_number_field->val_int());
+ nr= table->next_number_field->val_int();
+ }
+ if (append)
+ {
+ auto_inc_interval_for_cur_row.replace(nr, nb_reserved_values,
+ variables->auto_increment_increment);
+ /* Row-based replication does not need to store intervals in binlog */
+ if (!thd->current_stmt_binlog_row_based)
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(),
+ auto_inc_interval_for_cur_row.values(),
+ variables->auto_increment_increment);
}
- else
- DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
/*
- We can't set next_insert_id if the auto-increment key is not the
- first key part, as there is no guarantee that the first parts will be in
- sequence
+ Record this autogenerated value. If the caller then
+ succeeds to insert this value, it will call
+ record_first_successful_insert_id_in_cur_stmt()
+ which will set first_successful_insert_id_in_cur_stmt if it's not
+ already set.
*/
- if (!table->s->next_number_key_offset)
- {
- /*
- Set next insert id to point to next auto-increment value to be able to
- handle multi-row statements
- This works even if auto_increment_increment > 1
- */
- thd->next_insert_id= next_insert_id(nr, variables);
- }
- else
- thd->next_insert_id= 0;
+ insert_id_for_cur_row= nr;
+ /*
+ Set next insert id to point to next auto-increment value to be able to
+ handle multi-row statements.
+ */
+ set_next_insert_id(compute_next_insert_id(nr, variables));
- /* Mark that we generated a new value */
- auto_increment_column_changed=1;
DBUG_RETURN(0);
}
+
/*
- restore_auto_increment
+ MySQL signal that it changed the column bitmap
+
+ USAGE
+ This is for handlers that needs to setup their own column bitmaps.
+ Normally the handler should set up their own column bitmaps in
+ index_init() or rnd_init() and in any column_bitmaps_signal() call after
+ this.
- In case of error on write, we restore the last used next_insert_id value
- because the previous value was not used.
+ The handler is allowd to do changes to the bitmap after a index_init or
+ rnd_init() call is made as after this, MySQL will not use the bitmap
+ for any program logic checking.
*/
-void handler::restore_auto_increment()
+void handler::column_bitmaps_signal()
{
- THD *thd= table->in_use;
- if (thd->next_insert_id)
- thd->next_insert_id= thd->prev_insert_id;
+ DBUG_ENTER("column_bitmaps_signal");
+ DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", (long) table->read_set,
+ (long) table->write_set));
+ DBUG_VOID_RETURN;
}
-ulonglong handler::get_auto_increment()
+/*
+ Reserves an interval of auto_increment values from the handler.
+
+ SYNOPSIS
+ get_auto_increment()
+ offset
+ increment
+ nb_desired_values how many values we want
+ first_value (OUT) the first value reserved by the handler
+ nb_reserved_values (OUT) how many values the handler reserved
+
+ offset and increment means that we want values to be of the form
+ offset + N * increment, where N>=0 is integer.
+ If the function sets *first_value to ~(ulonglong)0 it means an error.
+ If the function sets *nb_reserved_values to ULONGLONG_MAX it means it has
+ reserved to "positive infinite".
+*/
+
+void handler::get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
{
ulonglong nr;
int error;
(void) extra(HA_EXTRA_KEYREAD);
- index_init(table->s->next_number_index);
+ table->mark_columns_used_by_index_no_reset(table->s->next_number_index,
+ table->read_set);
+ column_bitmaps_signal();
+ index_init(table->s->next_number_index, 1);
if (!table->s->next_number_key_offset)
{ // Autoincrement at key-start
error=index_last(table->record[1]);
+ /*
+ MySQL implicitely assumes such method does locking (as MySQL decides to
+ use nr+increment without checking again with the handler, in
+ handler::update_auto_increment()), so reserves to infinite.
+ */
+ *nb_reserved_values= ULONGLONG_MAX;
}
else
{
@@ -1717,6 +1981,13 @@ ulonglong handler::get_auto_increment()
table->s->next_number_key_offset);
error= index_read(table->record[1], key, table->s->next_number_key_offset,
HA_READ_PREFIX_LAST);
+ /*
+ MySQL needs to call us for next row: assume we are inserting ("a",null)
+ here, we return 3, and next this statement will want to insert
+ ("b",null): there is no reason why ("b",3+1) would be the good row to
+ insert: maybe it already exists, maybe 3+1 is too large...
+ */
+ *nb_reserved_values= 1;
}
if (error)
@@ -1726,7 +1997,53 @@ ulonglong handler::get_auto_increment()
val_int_offset(table->s->rec_buff_length)+1);
index_end();
(void) extra(HA_EXTRA_NO_KEYREAD);
- return nr;
+ *first_value= nr;
+}
+
+
+void handler::ha_release_auto_increment()
+{
+ release_auto_increment();
+ insert_id_for_cur_row= 0;
+ auto_inc_interval_for_cur_row.replace(0, 0, 0);
+ if (next_insert_id > 0)
+ {
+ next_insert_id= 0;
+ /*
+ this statement used forced auto_increment values if there were some,
+ wipe them away for other statements.
+ */
+ table->in_use->auto_inc_intervals_forced.empty();
+ }
+}
+
+
+void handler::print_keydup_error(uint key_nr, const char *msg)
+{
+ /* Write the duplicated key in the error message */
+ char key[MAX_KEY_LENGTH];
+ String str(key,sizeof(key),system_charset_info);
+
+ if (key_nr == MAX_KEY)
+ {
+ /* Key is unknown */
+ str.copy("", 0, system_charset_info);
+ my_printf_error(ER_DUP_ENTRY, msg,
+ MYF(0), str.c_ptr(), "*UNKNOWN*");
+ }
+ else
+ {
+ /* Table is opened and defined at this point */
+ key_unpack(&str,table,(uint) key_nr);
+ uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg);
+ if (str.length() >= max_length)
+ {
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
+ }
+ my_printf_error(ER_DUP_ENTRY, msg,
+ MYF(0), str.c_ptr(), table->key_info[key_nr].name);
+ }
}
@@ -1769,30 +2086,35 @@ void handler::print_error(int error, myf errflag)
uint key_nr=get_dup_key(error);
if ((int) key_nr >= 0)
{
- /* Write the dupplicated key in the error message */
+ print_keydup_error(key_nr, ER(ER_DUP_ENTRY));
+ DBUG_VOID_RETURN;
+ }
+ textno=ER_DUP_KEY;
+ break;
+ }
+ case HA_ERR_FOREIGN_DUPLICATE_KEY:
+ {
+ uint key_nr= get_dup_key(error);
+ if ((int) key_nr >= 0)
+ {
+ uint max_length;
+ /* Write the key in the error message */
char key[MAX_KEY_LENGTH];
String str(key,sizeof(key),system_charset_info);
-
- if (key_nr == MAX_KEY)
- {
- /* Key is unknown */
- str.copy("", 0, system_charset_info);
- key_nr= (uint) -1;
- }
- else
+ /* Table is opened and defined at this point */
+ key_unpack(&str,table,(uint) key_nr);
+ max_length= (MYSQL_ERRMSG_SIZE-
+ (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
+ if (str.length() >= max_length)
{
- key_unpack(&str,table,(uint) key_nr);
- uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_DUP_ENTRY));
- if (str.length() >= max_length)
- {
- str.length(max_length-4);
- str.append(STRING_WITH_LEN("..."));
- }
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
}
- my_error(ER_DUP_ENTRY, MYF(0), str.c_ptr(), key_nr+1);
+ my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table_share->table_name.str,
+ str.c_ptr(), key_nr+1);
DBUG_VOID_RETURN;
}
- textno=ER_DUP_KEY;
+ textno= ER_DUP_KEY;
break;
}
case HA_ERR_NULL_IN_SPATIAL:
@@ -1868,19 +2190,20 @@ void handler::print_error(int error, myf errflag)
textno=ER_TABLE_DEF_CHANGED;
break;
case HA_ERR_NO_SUCH_TABLE:
- {
- /*
- We have to use path to find database name instead of using
- table->table_cache_key because if the table didn't exist, then
- table_cache_key was not set up
- */
- char *db;
- char buff[FN_REFLEN];
- uint length= dirname_part(buff,table->s->path);
- buff[length-1]=0;
- db=buff+dirname_length(buff);
- my_error(ER_NO_SUCH_TABLE, MYF(0), db, table->alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_share->db.str,
+ table_share->table_name.str);
break;
+ case HA_ERR_RBR_LOGGING_FAILED:
+ textno= ER_BINLOG_ROW_LOGGING_FAILED;
+ break;
+ case HA_ERR_DROP_INDEX_FK:
+ {
+ const char *ptr= "???";
+ uint key_nr= get_dup_key(error);
+ if ((int) key_nr >= 0)
+ ptr= table->key_info[key_nr].name;
+ my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
+ DBUG_VOID_RETURN;
}
case HA_ERR_TABLE_NEEDS_UPGRADE:
textno=ER_TABLE_NEEDS_UPGRADE;
@@ -1914,7 +2237,7 @@ void handler::print_error(int error, myf errflag)
DBUG_VOID_RETURN;
}
}
- my_error(textno, errflag, table->alias, error);
+ my_error(textno, errflag, table_share->table_name.str, error);
DBUG_VOID_RETURN;
}
@@ -1954,7 +2277,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
if (!keypart->fieldnr)
continue;
Field *field= table->field[keypart->fieldnr-1];
- if (field->type() == FIELD_TYPE_BLOB)
+ if (field->type() == MYSQL_TYPE_BLOB)
{
if (check_opt->sql_flags & TT_FOR_UPGRADE)
check_opt->flags= T_MEDIUM;
@@ -1976,7 +2299,7 @@ int handler::check_old_types()
/* check for bad DECIMAL field */
for (field= table->field; (*field); field++)
{
- if ((*field)->type() == FIELD_TYPE_NEWDECIMAL)
+ if ((*field)->type() == MYSQL_TYPE_NEWDECIMAL)
{
return HA_ADMIN_NEEDS_ALTER;
}
@@ -2000,10 +2323,7 @@ static bool update_frm_version(TABLE *table, bool needs_lock)
if (table->s->mysql_version != MYSQL_VERSION_ID)
DBUG_RETURN(0);
- strxnmov(path, sizeof(path)-1, mysql_data_home, "/", table->s->db, "/",
- table->s->table_name, reg_ext, NullS);
- if (!unpack_filename(path, path))
- DBUG_RETURN(1);
+ strxmov(path, table->s->normalized_path.str, reg_ext, NullS);
if (needs_lock)
pthread_mutex_lock(&LOCK_open);
@@ -2011,8 +2331,8 @@ static bool update_frm_version(TABLE *table, bool needs_lock)
if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
{
uchar version[4];
- char *key= table->s->table_cache_key;
- uint key_length= table->s->key_length;
+ char *key= table->s->table_cache_key.str;
+ uint key_length= table->s->table_cache_key.length;
TABLE *entry;
HASH_SEARCH_STATE state;
@@ -2042,8 +2362,9 @@ uint handler::get_dup_key(int error)
{
DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1;
- if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE ||
- error == HA_ERR_NULL_IN_SPATIAL)
+ if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
+ error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL ||
+ error == HA_ERR_DROP_INDEX_FK)
info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
DBUG_RETURN(table->file->errkey);
}
@@ -2074,7 +2395,7 @@ int handler::delete_table(const char *name)
for (const char **ext=bas_ext(); *ext ; ext++)
{
- fn_format(buff, name, "", *ext, 2 | 4);
+ fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
if (my_delete_with_symlink(buff, MYF(0)))
{
if ((error= my_errno) != ENOENT)
@@ -2104,6 +2425,13 @@ int handler::rename_table(const char * from, const char * to)
}
+void handler::drop_table(const char *name)
+{
+ close();
+ delete_table(name);
+}
+
+
/*
Performs checks upon the table.
@@ -2198,31 +2526,67 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen)
}
+void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info,
+ uint part_id)
+{
+ info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE |
+ HA_STATUS_NO_LOCK);
+ stat_info->records= stats.records;
+ stat_info->mean_rec_length= stats.mean_rec_length;
+ stat_info->data_file_length= stats.data_file_length;
+ stat_info->max_data_file_length= stats.max_data_file_length;
+ stat_info->index_file_length= stats.index_file_length;
+ stat_info->delete_length= stats.delete_length;
+ stat_info->create_time= stats.create_time;
+ stat_info->update_time= stats.update_time;
+ stat_info->check_time= stats.check_time;
+ stat_info->check_sum= 0;
+ if (table_flags() & (ulong) HA_HAS_CHECKSUM)
+ stat_info->check_sum= checksum();
+ return;
+}
+
+
/****************************************************************************
** 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 appropriate database-creator
+
+ NOTES
+ We must have a write lock on LOCK_open to be sure no other thread
+ interferes with table
+
+ RETURN
+ 0 ok
+ 1 error
*/
-int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+int ha_create_table(THD *thd, const char *path,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
bool update_create_info)
{
- int error;
+ int error= 1;
TABLE table;
char name_buff[FN_REFLEN];
+ const char *name;
+ TABLE_SHARE share;
DBUG_ENTER("ha_create_table");
+
+ init_tmp_table_share(&share, db, 0, table_name, path);
+ if (open_table_def(thd, &share, 0) ||
+ open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
+ TRUE))
+ goto err;
- if (openfrm(current_thd, name,"",0,(uint) READ_ALL, 0, &table))
- DBUG_RETURN(1);
if (update_create_info)
- {
update_create_info_from_table(create_info, &table);
- }
+
+ name= share.path.str;
if (lower_case_table_names == 2 &&
- !(table.file->table_flags() & HA_FILE_BASED))
+ !(table.file->ha_table_flags() & HA_FILE_BASED))
{
/* Ensure that handler gets name in lower case */
strmov(name_buff, name);
@@ -2230,27 +2594,32 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
name= name_buff;
}
- error=table.file->create(name,&table,create_info);
- VOID(closefrm(&table));
+ error= table.file->create(name, &table, create_info);
+ VOID(closefrm(&table, 0));
if (error)
- my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name,error);
+ {
+ strxmov(name_buff, db, ".", table_name, NullS);
+ my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
+ }
+err:
+ free_table_share(&share);
DBUG_RETURN(error != 0);
}
/*
- Try to discover table from engine and
- if found, write the frm file to disk.
+ Try to discover table from engine
+
+ NOTES
+ If found, write the frm file to disk.
RETURN VALUES:
- -1 : Table did not exists
- 0 : Table created ok
- > 0 : Error, table existed but could not be created
+ -1 Table did not exists
+ 0 Table created ok
+ > 0 Error, table existed but could not be created
*/
-int ha_create_table_from_engine(THD* thd,
- const char *db,
- const char *name)
+int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
{
int error;
const void *frmblob;
@@ -2258,6 +2627,7 @@ int ha_create_table_from_engine(THD* thd,
char path[FN_REFLEN];
HA_CREATE_INFO create_info;
TABLE table;
+ TABLE_SHARE share;
DBUG_ENTER("ha_create_table_from_engine");
DBUG_PRINT("enter", ("name '%s'.'%s'", db, name));
@@ -2273,27 +2643,35 @@ int ha_create_table_from_engine(THD* thd,
frmblob and frmlen are set, write the frm to disk
*/
- (void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS);
+ (void)strxnmov(path,FN_REFLEN-1,mysql_data_home,"/",db,"/",name,NullS);
// Save the frm file
error= writefrm(path, frmblob, frmlen);
my_free((char*) frmblob, MYF(0));
if (error)
DBUG_RETURN(2);
- if (openfrm(thd, path,"",0,(uint) READ_ALL, 0, &table))
+ init_tmp_table_share(&share, db, 0, name, path);
+ if (open_table_def(thd, &share, 0))
+ {
+ DBUG_RETURN(3);
+ }
+ if (open_table_from_share(thd, &share, "" ,0, 0, 0, &table, FALSE))
+ {
+ free_table_share(&share);
DBUG_RETURN(3);
+ }
update_create_info_from_table(&create_info, &table);
create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE;
if (lower_case_table_names == 2 &&
- !(table.file->table_flags() & HA_FILE_BASED))
+ !(table.file->ha_table_flags() & HA_FILE_BASED))
{
/* Ensure that handler gets name in lower case */
my_casedn_str(files_charset_info, path);
}
error=table.file->create(path,&table,&create_info);
- VOID(closefrm(&table));
+ VOID(closefrm(&table, 1));
DBUG_RETURN(error != 0);
}
@@ -2403,18 +2781,43 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
>0 : error. frmblob and frmlen may not be set
*/
+struct st_discover_args
+{
+ const char *db;
+ const char *name;
+ const void** frmblob;
+ uint* frmlen;
+};
+
+static my_bool discover_handlerton(THD *thd, st_plugin_int *plugin,
+ void *arg)
+{
+ st_discover_args *vargs= (st_discover_args *)arg;
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->discover &&
+ (!(hton->discover(hton, thd, vargs->db, vargs->name,
+ vargs->frmblob,
+ vargs->frmlen))))
+ return TRUE;
+
+ return FALSE;
+}
+
int ha_discover(THD *thd, const char *db, 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", db, name));
+ st_discover_args args= {db, name, frmblob, frmlen};
+
if (is_prefix(name,tmp_file_prefix)) /* skip temporary tables */
DBUG_RETURN(error);
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error= ndbcluster_discover(thd, db, name, frmblob, frmlen);
-#endif
+
+ if (plugin_foreach(thd, discover_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args))
+ error= 0;
+
if (!error)
statistic_increment(thd->status_var.ha_discover_count,&LOCK_status);
DBUG_RETURN(error);
@@ -2422,10 +2825,33 @@ int ha_discover(THD *thd, const char *db, const char *name,
/*
- Call this function in order to give the handler the possiblity
- to ask engine if there are any new tables that should be written to disk
+ Call this function in order to give the handler the possibility
+ to ask engine if there are any new tables that should be written to disk
or any dropped tables that need to be removed from disk
*/
+struct st_find_files_args
+{
+ const char *db;
+ const char *path;
+ const char *wild;
+ bool dir;
+ List<char> *files;
+};
+
+static my_bool find_files_handlerton(THD *thd, st_plugin_int *plugin,
+ void *arg)
+{
+ st_find_files_args *vargs= (st_find_files_args *)arg;
+ handlerton *hton= (handlerton *)plugin->data;
+
+
+ if (hton->state == SHOW_OPTION_YES && hton->find_files)
+ if (hton->find_files(hton, thd, vargs->db, vargs->path, vargs->wild,
+ vargs->dir, vargs->files))
+ return TRUE;
+
+ return FALSE;
+}
int
ha_find_files(THD *thd,const char *db,const char *path,
@@ -2433,12 +2859,13 @@ ha_find_files(THD *thd,const char *db,const char *path,
{
int error= 0;
DBUG_ENTER("ha_find_files");
- DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d",
+ DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d",
db, path, wild, dir));
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error= ndbcluster_find_files(thd, db, path, wild, dir, files);
-#endif
+ st_find_files_args args= {db, path, wild, dir, files};
+
+ plugin_foreach(thd, find_files_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+ /* The return value is not currently used */
DBUG_RETURN(error);
}
@@ -2452,19 +2879,175 @@ ha_find_files(THD *thd,const char *db,const char *path,
# Error code
*/
+
+struct st_table_exists_in_engine_args
+{
+ const char *db;
+ const char *name;
+};
+
+static my_bool table_exists_in_engine_handlerton(THD *thd, st_plugin_int *plugin,
+ void *arg)
+{
+ st_table_exists_in_engine_args *vargs= (st_table_exists_in_engine_args *)arg;
+ handlerton *hton= (handlerton *)plugin->data;
+
+ if (hton->state == SHOW_OPTION_YES && hton->table_exists_in_engine)
+ if ((hton->table_exists_in_engine(hton, thd, vargs->db, vargs->name)) == 1)
+ return TRUE;
+
+ return FALSE;
+}
+
int ha_table_exists_in_engine(THD* thd, const char* db, const char* name)
{
int error= 0;
DBUG_ENTER("ha_table_exists_in_engine");
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error= ndbcluster_table_exists_in_engine(thd, db, name);
-#endif
+ st_table_exists_in_engine_args args= {db, name};
+ error= plugin_foreach(thd, table_exists_in_engine_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
DBUG_PRINT("exit", ("error: %d", error));
DBUG_RETURN(error);
}
+#ifdef HAVE_NDB_BINLOG
+/*
+ TODO: change this into a dynamic struct
+ List<handlerton> does not work as
+ 1. binlog_end is called when MEM_ROOT is gone
+ 2. cannot work with thd MEM_ROOT as memory should be freed
+*/
+#define MAX_HTON_LIST_ST 63
+struct hton_list_st
+{
+ handlerton *hton[MAX_HTON_LIST_ST];
+ uint sz;
+};
+
+struct binlog_func_st
+{
+ enum_binlog_func fn;
+ void *arg;
+};
+
+/*
+ Listing handlertons first to avoid recursive calls and deadlock
+*/
+static my_bool binlog_func_list(THD *thd, st_plugin_int *plugin, void *arg)
+{
+ hton_list_st *hton_list= (hton_list_st *)arg;
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->binlog_func)
+ {
+ uint sz= hton_list->sz;
+ if (sz == MAX_HTON_LIST_ST-1)
+ {
+ /* list full */
+ return FALSE;
+ }
+ hton_list->hton[sz]= hton;
+ hton_list->sz= sz+1;
+ }
+ return FALSE;
+}
+
+static my_bool binlog_func_foreach(THD *thd, binlog_func_st *bfn)
+{
+ hton_list_st hton_list;
+ uint i, sz;
+
+ hton_list.sz= 0;
+ plugin_foreach(thd, binlog_func_list,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &hton_list);
+
+ for (i= 0, sz= hton_list.sz; i < sz ; i++)
+ hton_list.hton[i]->binlog_func(hton_list.hton[i], thd, bfn->fn, bfn->arg);
+ return FALSE;
+}
+
+int ha_reset_logs(THD *thd)
+{
+ binlog_func_st bfn= {BFN_RESET_LOGS, 0};
+ binlog_func_foreach(thd, &bfn);
+ return 0;
+}
+
+void ha_reset_slave(THD* thd)
+{
+ binlog_func_st bfn= {BFN_RESET_SLAVE, 0};
+ binlog_func_foreach(thd, &bfn);
+}
+
+void ha_binlog_wait(THD* thd)
+{
+ binlog_func_st bfn= {BFN_BINLOG_WAIT, 0};
+ binlog_func_foreach(thd, &bfn);
+}
+
+int ha_binlog_end(THD* thd)
+{
+ binlog_func_st bfn= {BFN_BINLOG_END, 0};
+ binlog_func_foreach(thd, &bfn);
+ return 0;
+}
+
+int ha_binlog_index_purge_file(THD *thd, const char *file)
+{
+ binlog_func_st bfn= {BFN_BINLOG_PURGE_FILE, (void *)file};
+ binlog_func_foreach(thd, &bfn);
+ return 0;
+}
+
+struct binlog_log_query_st
+{
+ enum_binlog_command binlog_command;
+ const char *query;
+ uint query_length;
+ const char *db;
+ const char *table_name;
+};
+
+static my_bool binlog_log_query_handlerton2(THD *thd,
+ handlerton *hton,
+ void *args)
+{
+ struct binlog_log_query_st *b= (struct binlog_log_query_st*)args;
+ if (hton->state == SHOW_OPTION_YES && hton->binlog_log_query)
+ hton->binlog_log_query(hton, thd,
+ b->binlog_command,
+ b->query,
+ b->query_length,
+ b->db,
+ b->table_name);
+ return FALSE;
+}
+
+static my_bool binlog_log_query_handlerton(THD *thd,
+ st_plugin_int *plugin,
+ void *args)
+{
+ return binlog_log_query_handlerton2(thd, (handlerton *)plugin->data, args);
+}
+
+void ha_binlog_log_query(THD *thd, handlerton *hton,
+ enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name)
+{
+ struct binlog_log_query_st b;
+ b.binlog_command= binlog_command;
+ b.query= query;
+ b.query_length= query_length;
+ b.db= db;
+ b.table_name= table_name;
+ if (hton == 0)
+ plugin_foreach(thd, binlog_log_query_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &b);
+ else
+ binlog_log_query_handlerton2(thd, hton, &b);
+}
+#endif
/*
Read the first row of a multi-range set.
@@ -2500,6 +3083,9 @@ int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
multi_range_sorted= sorted;
multi_range_buffer= buffer;
+ table->mark_columns_used_by_index_no_reset(active_index, table->read_set);
+ table->column_bitmaps_set(table->read_set, table->write_set);
+
for (multi_range_curr= ranges, multi_range_end= ranges + range_count;
multi_range_curr < multi_range_end;
multi_range_curr++)
@@ -2599,7 +3185,7 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
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
+ eq_range_arg Set to 1 if start_key == end_key
sorted Set to 1 if result should be sorted per key
NOTES
@@ -2637,7 +3223,7 @@ int handler::read_range_first(const key_range *start_key,
start_key->length,
start_key->flag);
if (result)
- DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND)
? HA_ERR_END_OF_FILE
: result);
@@ -2685,7 +3271,7 @@ int handler::read_range_next()
SYNOPSIS
compare_key
range range to compare to row. May be 0 for no range
-
+
NOTES
See key.cc::key_cmp() for details
@@ -2711,7 +3297,7 @@ int handler::compare_key(key_range *range)
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);
+ int error= ha_index_init(index, 0);
if (!error)
error= index_read(buf, key, key_len, find_flag);
if (!error)
@@ -2735,40 +3321,50 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key,
pointer pointer to TYPELIB structure
*/
+static my_bool exts_handlerton(THD *unused, st_plugin_int *plugin,
+ void *arg)
+{
+ List<char> *found_exts= (List<char> *) arg;
+ handlerton *hton= (handlerton *)plugin->data;
+ handler *file;
+ if (hton->state == SHOW_OPTION_YES && hton->create &&
+ (file= hton->create(hton, (TABLE_SHARE*) 0, current_thd->mem_root)))
+ {
+ List_iterator_fast<char> it(*found_exts);
+ const char **ext, *old_ext;
+
+ for (ext= file->bas_ext(); *ext; ext++)
+ {
+ while ((old_ext= it++))
+ {
+ if (!strcmp(old_ext, *ext))
+ break;
+ }
+ if (!old_ext)
+ found_exts->push_back((char *) *ext);
+
+ it.rewind();
+ }
+ delete file;
+ }
+ return FALSE;
+}
+
TYPELIB *ha_known_exts(void)
{
MEM_ROOT *mem_root= current_thd->mem_root;
if (!known_extensions.type_names || mysys_usage_id != known_extensions_id)
{
- handlerton **types;
List<char> found_exts;
- List_iterator_fast<char> it(found_exts);
const char **ext, *old_ext;
known_extensions_id= mysys_usage_id;
found_exts.push_back((char*) triggers_file_ext);
found_exts.push_back((char*) trigname_file_ext);
- for (types= sys_table_types; *types; types++)
- {
- if ((*types)->state == SHOW_OPTION_YES)
- {
- handler *file= get_new_handler(0, mem_root,
- (enum db_type) (*types)->db_type);
- for (ext= file->bas_ext(); *ext; ext++)
- {
- while ((old_ext= it++))
- {
- if (!strcmp(old_ext, *ext))
- break;
- }
- if (!old_ext)
- found_exts.push_back((char *) *ext);
- it.rewind();
- }
- delete file;
- }
- }
+ plugin_foreach(NULL, exts_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);
+
ext= (const char **) my_once_alloc(sizeof(char *)*
(found_exts.elements+1),
MYF(MY_WME | MY_FAE));
@@ -2777,9 +3373,518 @@ TYPELIB *ha_known_exts(void)
known_extensions.count= found_exts.elements;
known_extensions.type_names= ext;
+ List_iterator_fast<char> it(found_exts);
while ((old_ext= it++))
*ext++= old_ext;
*ext= 0;
}
return &known_extensions;
}
+
+
+static bool stat_print(THD *thd, const char *type, uint type_len,
+ const char *file, uint file_len,
+ const char *status, uint status_len)
+{
+ Protocol *protocol= thd->protocol;
+ protocol->prepare_for_resend();
+ protocol->store(type, type_len, system_charset_info);
+ protocol->store(file, file_len, system_charset_info);
+ protocol->store(status, status_len, system_charset_info);
+ if (protocol->write())
+ return TRUE;
+ return FALSE;
+}
+
+
+static my_bool showstat_handlerton(THD *thd, st_plugin_int *plugin,
+ void *arg)
+{
+ enum ha_stat_type stat= *(enum ha_stat_type *) arg;
+ handlerton *hton= (handlerton *)plugin->data;
+ if (hton->state == SHOW_OPTION_YES && hton->show_status &&
+ hton->show_status(hton, thd, stat_print, stat))
+ return TRUE;
+ return FALSE;
+}
+
+bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
+{
+ List<Item> field_list;
+ Protocol *protocol= thd->protocol;
+ bool result;
+
+ field_list.push_back(new Item_empty_string("Type",10));
+ field_list.push_back(new Item_empty_string("Name",FN_REFLEN));
+ field_list.push_back(new Item_empty_string("Status",10));
+
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ return TRUE;
+
+ if (db_type == NULL)
+ {
+ result= plugin_foreach(thd, showstat_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &stat);
+ }
+ else
+ {
+ if (db_type->state != SHOW_OPTION_YES)
+ {
+ const LEX_STRING *name=&hton2plugin[db_type->slot]->name;
+ result= stat_print(thd, name->str, name->length,
+ "", 0, "DISABLED", 8) ? 1 : 0;
+ }
+ else
+ result= db_type->show_status &&
+ db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0;
+ }
+
+ if (!result)
+ send_eof(thd);
+ return result;
+}
+
+/*
+ Function to check if the conditions for row-based binlogging is
+ correct for the table.
+
+ A row in the given table should be replicated if:
+ - Row-based replication is enabled in the current thread
+ - The binlog is enabled
+ - It is not a temporary table
+ - The binary log is open
+ - The database the table resides in shall be binlogged (binlog_*_db rules)
+ - table is not mysql.event
+*/
+
+#ifdef HAVE_ROW_BASED_REPLICATION
+/* The Sun compiler cannot instantiate the template below if this is
+ declared static, but it works by putting it into an anonymous
+ namespace. */
+namespace {
+ bool check_table_binlog_row_based(THD *thd, TABLE *table)
+ {
+ if (table->s->cached_row_logging_check == -1)
+ {
+ int const check(table->s->tmp_table == NO_TMP_TABLE &&
+ binlog_filter->db_ok(table->s->db.str) &&
+ strcmp("mysql", table->s->db.str) != 0);
+ table->s->cached_row_logging_check= check;
+ }
+
+ DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
+ table->s->cached_row_logging_check == 1);
+
+ return (thd->current_stmt_binlog_row_based &&
+ (thd->options & OPTION_BIN_LOG) &&
+ mysql_bin_log.is_open() &&
+ table->s->cached_row_logging_check);
+ }
+}
+
+/*
+ Write table maps for all (manually or automatically) locked tables
+ to the binary log.
+
+ SYNOPSIS
+ write_locked_table_maps()
+ thd Pointer to THD structure
+
+ DESCRIPTION
+ This function will generate and write table maps for all tables
+ that are locked by the thread 'thd'. Either manually locked
+ (stored in THD::locked_tables) and automatically locked (stored
+ in THD::lock) are considered.
+
+ RETURN VALUE
+ 0 All OK
+ 1 Failed to write all table maps
+
+ SEE ALSO
+ THD::lock
+ THD::locked_tables
+ */
+namespace
+{
+ int write_locked_table_maps(THD *thd)
+ {
+ DBUG_ENTER("write_locked_table_maps");
+ DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx "
+ "thd->extra_lock: 0x%lx",
+ (long) thd, (long) thd->lock,
+ (long) thd->locked_tables, (long) thd->extra_lock));
+
+ if (thd->get_binlog_table_maps() == 0)
+ {
+ MYSQL_LOCK *locks[3];
+ locks[0]= thd->extra_lock;
+ locks[1]= thd->lock;
+ locks[2]= thd->locked_tables;
+ for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
+ {
+ MYSQL_LOCK const *const lock= locks[i];
+ if (lock == NULL)
+ continue;
+
+ TABLE **const end_ptr= lock->table + lock->table_count;
+ for (TABLE **table_ptr= lock->table ;
+ table_ptr != end_ptr ;
+ ++table_ptr)
+ {
+ TABLE *const table= *table_ptr;
+ DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
+ if (table->current_lock == F_WRLCK &&
+ check_table_binlog_row_based(thd, table))
+ {
+ int const has_trans= table->file->has_transactions();
+ int const error= thd->binlog_write_table_map(table, has_trans);
+ /*
+ If an error occurs, it is the responsibility of the caller to
+ roll back the transaction.
+ */
+ if (unlikely(error))
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ }
+ DBUG_RETURN(0);
+ }
+
+ template<class RowsEventT> int
+ binlog_log_row(TABLE* table,
+ const byte *before_record,
+ const byte *after_record)
+ {
+ if (table->file->ha_table_flags() & HA_HAS_OWN_BINLOGGING)
+ return 0;
+ bool error= 0;
+ THD *const thd= table->in_use;
+
+ if (check_table_binlog_row_based(thd, table))
+ {
+ MY_BITMAP cols;
+ /* Potential buffer on the stack for the bitmap */
+ uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)];
+ uint n_fields= table->s->fields;
+ my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;
+
+ /*
+ If there are no table maps written to the binary log, this is
+ the first row handled in this statement. In that case, we need
+ to write table maps for all locked tables to the binary log.
+ */
+ if (likely(!(error= bitmap_init(&cols,
+ use_bitbuf ? bitbuf : NULL,
+ (n_fields + 7) & ~7UL,
+ FALSE))))
+ {
+ bitmap_set_all(&cols);
+ if (likely(!(error= write_locked_table_maps(thd))))
+ {
+ error=
+ RowsEventT::binlog_row_logging_function(thd, table,
+ table->file->
+ has_transactions(),
+ &cols, table->s->fields,
+ before_record,
+ after_record);
+ }
+ if (!use_bitbuf)
+ bitmap_free(&cols);
+ }
+ }
+ return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
+ }
+
+ /*
+ Instantiate the versions we need for the above template function,
+ because we have -fno-implicit-template as compiling option.
+ */
+
+ template int
+ binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *);
+
+ template int
+ binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *);
+
+ template int
+ binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *);
+}
+
+#endif /* HAVE_ROW_BASED_REPLICATION */
+
+int handler::ha_external_lock(THD *thd, int lock_type)
+{
+ DBUG_ENTER("handler::ha_external_lock");
+ /*
+ Whether this is lock or unlock, this should be true, and is to verify that
+ if get_auto_increment() was called (thus may have reserved intervals or
+ taken a table lock), ha_release_auto_increment() was too.
+ */
+ DBUG_ASSERT(next_insert_id == 0);
+ DBUG_RETURN(external_lock(thd, lock_type));
+}
+
+
+/*
+ Check handler usage and reset state of file to after 'open'
+*/
+
+int handler::ha_reset()
+{
+ DBUG_ENTER("ha_reset");
+ /* Check that we have called all proper deallocation functions */
+ DBUG_ASSERT((byte*) table->def_read_set.bitmap +
+ table->s->column_bitmap_size ==
+ (byte*) table->def_write_set.bitmap);
+ DBUG_ASSERT(bitmap_is_set_all(&table->s->all_set));
+ DBUG_ASSERT(table->key_read == 0);
+ /* ensure that ha_index_end / ha_rnd_end has been called */
+ DBUG_ASSERT(inited == NONE);
+ /* Free cache used by filesort */
+ free_io_cache(table);
+ DBUG_RETURN(reset());
+}
+
+
+int handler::ha_write_row(byte *buf)
+{
+ int error;
+ if (unlikely(error= write_row(buf)))
+ return error;
+#ifdef HAVE_ROW_BASED_REPLICATION
+ if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf)))
+ return error;
+#endif
+ return 0;
+}
+
+int handler::ha_update_row(const byte *old_data, byte *new_data)
+{
+ int error;
+
+ /*
+ Some storage engines require that the new record is in record[0]
+ (and the old record is in record[1]).
+ */
+ DBUG_ASSERT(new_data == table->record[0]);
+
+ if (unlikely(error= update_row(old_data, new_data)))
+ return error;
+#ifdef HAVE_ROW_BASED_REPLICATION
+ if (unlikely(error= binlog_log_row<Update_rows_log_event>(table, old_data, new_data)))
+ return error;
+#endif
+ return 0;
+}
+
+int handler::ha_delete_row(const byte *buf)
+{
+ int error;
+ if (unlikely(error= delete_row(buf)))
+ return error;
+#ifdef HAVE_ROW_BASED_REPLICATION
+ if (unlikely(error= binlog_log_row<Delete_rows_log_event>(table, buf, 0)))
+ return error;
+#endif
+ return 0;
+}
+
+
+
+/*
+ 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
+*/
+
+void handler::use_hidden_primary_key()
+{
+ /* fallback to use all columns in the table to identify row */
+ table->use_all_columns();
+}
+
+
+/*
+ Dummy function which accept information about log files which is not need
+ by handlers
+*/
+
+void signal_log_not_needed(struct handlerton, char *log_file)
+{
+ DBUG_ENTER("signal_log_not_needed");
+ DBUG_PRINT("enter", ("logfile '%s'", log_file));
+ DBUG_VOID_RETURN;
+}
+
+
+#ifdef TRANS_LOG_MGM_EXAMPLE_CODE
+/*
+ Example of transaction log management functions based on assumption that logs
+ placed into a directory
+*/
+#include <my_dir.h>
+#include <my_sys.h>
+int example_of_iterator_using_for_logs_cleanup(handlerton *hton)
+{
+ void *buffer;
+ int res= 1;
+ struct handler_iterator iterator;
+ struct handler_log_file_data data;
+
+ if (!hton->create_iterator)
+ return 1; /* iterator creator is not supported */
+
+ if ((*hton->create_iterator)(hton, HA_TRANSACTLOG_ITERATOR, &iterator) !=
+ HA_ITERATOR_OK)
+ {
+ /* error during creation of log iterator or iterator is not supported */
+ return 1;
+ }
+ while((*iterator.next)(&iterator, (void*)&data) == 0)
+ {
+ printf("%s\n", data.filename.str);
+ if (data.status == HA_LOG_STATUS_FREE &&
+ my_delete(data.filename.str, MYF(MY_WME)))
+ goto err;
+ }
+ res= 0;
+err:
+ (*iterator.destroy)(&iterator);
+ return res;
+}
+
+
+/*
+ Here we should get info from handler where it save logs but here is
+ just example, so we use constant.
+ IMHO FN_ROOTDIR ("/") is safe enough for example, because nobody has
+ rights on it except root and it consist of directories only at lest for
+ *nix (sorry, can't find windows-safe solution here, but it is only example).
+*/
+#define fl_dir FN_ROOTDIR
+
+
+/*
+ Dummy function to return log status should be replaced by function which
+ really detect the log status and check that the file is a log of this
+ handler.
+*/
+
+enum log_status fl_get_log_status(char *log)
+{
+ MY_STAT stat_buff;
+ if (my_stat(log, &stat_buff, MYF(0)))
+ return HA_LOG_STATUS_INUSE;
+ return HA_LOG_STATUS_NOSUCHLOG;
+}
+
+
+struct fl_buff
+{
+ LEX_STRING *names;
+ enum log_status *statuses;
+ uint32 entries;
+ uint32 current;
+};
+
+
+int fl_log_iterator_next(struct handler_iterator *iterator,
+ void *iterator_object)
+{
+ struct fl_buff *buff= (struct fl_buff *)iterator->buffer;
+ struct handler_log_file_data *data=
+ (struct handler_log_file_data *) iterator_object;
+ if (buff->current >= buff->entries)
+ return 1;
+ data->filename= buff->names[buff->current];
+ data->status= buff->statuses[buff->current];
+ buff->current++;
+ return 0;
+}
+
+
+void fl_log_iterator_destroy(struct handler_iterator *iterator)
+{
+ my_free((gptr)iterator->buffer, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+
+/*
+ returns buffer, to be assigned in handler_iterator struct
+*/
+enum handler_create_iterator_result
+fl_log_iterator_buffer_init(struct handler_iterator *iterator)
+{
+ MY_DIR *dirp;
+ struct fl_buff *buff;
+ char *name_ptr;
+ byte *ptr;
+ FILEINFO *file;
+ uint32 i;
+
+ /* to be able to make my_free without crash in case of error */
+ iterator->buffer= 0;
+
+ if (!(dirp = my_dir(fl_dir, MYF(0))))
+ {
+ return HA_ITERATOR_ERROR;
+ }
+ if ((ptr= (byte*)my_malloc(ALIGN_SIZE(sizeof(fl_buff)) +
+ ((ALIGN_SIZE(sizeof(LEX_STRING)) +
+ sizeof(enum log_status) +
+ + FN_REFLEN) *
+ (uint) dirp->number_off_files),
+ MYF(0))) == 0)
+ {
+ return HA_ITERATOR_ERROR;
+ }
+ buff= (struct fl_buff *)ptr;
+ buff->entries= buff->current= 0;
+ ptr= ptr + (ALIGN_SIZE(sizeof(fl_buff)));
+ buff->names= (LEX_STRING*) (ptr);
+ ptr= ptr + ((ALIGN_SIZE(sizeof(LEX_STRING)) *
+ (uint) dirp->number_off_files));
+ buff->statuses= (enum log_status *)(ptr);
+ name_ptr= (char *)(ptr + (sizeof(enum log_status) *
+ (uint) dirp->number_off_files));
+ for (i=0 ; i < (uint) dirp->number_off_files ; i++)
+ {
+ enum log_status st;
+ file= dirp->dir_entry + i;
+ if ((file->name[0] == '.' &&
+ ((file->name[1] == '.' && file->name[2] == '\0') ||
+ file->name[1] == '\0')))
+ continue;
+ if ((st= fl_get_log_status(file->name)) == HA_LOG_STATUS_NOSUCHLOG)
+ continue;
+ name_ptr= strxnmov(buff->names[buff->entries].str= name_ptr,
+ FN_REFLEN, fl_dir, file->name, NullS);
+ buff->names[buff->entries].length= (name_ptr -
+ buff->names[buff->entries].str) - 1;
+ buff->statuses[buff->entries]= st;
+ buff->entries++;
+ }
+
+ iterator->buffer= buff;
+ iterator->next= &fl_log_iterator_next;
+ iterator->destroy= &fl_log_iterator_destroy;
+ return HA_ITERATOR_OK;
+}
+
+
+/* An example of a iterator creator */
+enum handler_create_iterator_result
+fl_create_iterator(enum handler_iterator_type type,
+ struct handler_iterator *iterator)
+{
+ switch(type) {
+ case HA_TRANSACTLOG_ITERATOR:
+ return fl_log_iterator_buffer_init(iterator);
+ default:
+ return HA_ITERATOR_UNSUPPORTED;
+ }
+}
+#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/