summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/ha_innodb.cc269
-rw-r--r--sql/ha_innodb.h5
-rw-r--r--sql/handler.cc40
-rw-r--r--sql/handler.h4
-rw-r--r--sql/log.cc33
-rw-r--r--sql/sql_class.h8
6 files changed, 251 insertions, 108 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 8941481a95f..a86bd21934b 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -14,8 +14,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* This file defines the InnoDB handler: the interface between MySQL and
-InnoDB */
+/*
+ This file defines the InnoDB handler: the interface between MySQL and InnoDB */
/* TODO list for the InnoDB handler:
- Ask Monty if strings of different languages can exist in the same
@@ -118,6 +118,36 @@ static void innobase_print_error(const char* db_errpfx, char* buffer);
/* General functions */
+/**********************************************************************
+Releases possible search latch, auto inc lock, and InnoDB thread FIFO ticket.
+These should be released at each SQL statement end. */
+static
+void
+innobase_release_stat_resources(
+/*============================*/
+ trx_t* trx) /* in: transaction object */
+{
+ if (trx->has_search_latch) {
+ trx_search_latch_release_if_reserved(trx);
+ }
+
+ if (trx->auto_inc_lock) {
+
+ /* If we had reserved the auto-inc lock for
+ some table in this SQL statement, we release it now */
+
+ srv_conc_enter_innodb(trx);
+ row_unlock_table_autoinc_for_mysql(trx);
+ srv_conc_exit_innodb(trx);
+ }
+
+ if (trx->declared_to_be_inside_innodb) {
+ /* Release our possible ticket in the FIFO */
+
+ srv_conc_force_exit_innodb(trx);
+ }
+}
+
/************************************************************************
Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
time calls srv_active_wake_master_thread. This function should be used
@@ -715,13 +745,12 @@ innobase_commit(
trx = check_trx_exists(thd);
if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) {
- srv_conc_enter_innodb(trx);
trx_commit_for_mysql(trx);
-
- srv_conc_exit_innodb();
}
+ /* Release possible statement level resources */
+ innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
#ifndef DBUG_OFF
@@ -738,6 +767,32 @@ innobase_commit(
}
/*********************************************************************
+This is called when MySQL writes the binlog entry for the current
+transaction. Writes to the InnoDB tablespace info which tells where the
+MySQL binlog entry for the current transaction ended. Also commits the
+transaction inside InnoDB. */
+
+int
+innobase_report_binlog_offset_and_commit(
+/*=====================================*/
+ /* out: 0 or error code */
+ THD* thd, /* in: user thread */
+ void* trx_handle, /* in: InnoDB trx handle */
+ char* log_file_name, /* in: latest binlog file name */
+ my_off_t end_offset) /* in: the offset in the binlog file
+ up to which we wrote */
+{
+ trx_t* trx;
+
+ trx = (trx_t*)trx_handle;
+
+ trx->mysql_log_file_name = log_file_name;
+ trx->mysql_log_offset = (ib_longlong)end_offset;
+
+ return(innobase_commit(thd, trx_handle));
+}
+
+/*********************************************************************
Rolls back a transaction in an InnoDB database. */
int
@@ -764,7 +819,10 @@ innobase_rollback(
error = trx_rollback_last_sql_stat_for_mysql(trx);
}
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(trx);
+
+ /* Release possible statement level resources */
+ innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
@@ -1437,6 +1495,8 @@ ha_innobase::write_row(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
+
+ innobase_release_stat_resources(prebuilt->trx);
}
if (table->next_number_field && record == table->record[0]) {
@@ -1487,7 +1547,7 @@ ha_innobase::write_row(
srv_conc_enter_innodb(prebuilt->trx);
error = row_lock_table_autoinc_for_mysql(prebuilt);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
if (error != DB_SUCCESS) {
@@ -1504,7 +1564,7 @@ ha_innobase::write_row(
error = row_lock_table_autoinc_for_mysql(
prebuilt);
if (error != DB_SUCCESS) {
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
error = convert_error_code_to_mysql(
error);
@@ -1513,7 +1573,7 @@ ha_innobase::write_row(
}
auto_inc = dict_table_autoinc_get(prebuilt->table);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
/* If auto_inc is now != 0 the autoinc counter
was already initialized for the table: we can give
@@ -1540,7 +1600,7 @@ ha_innobase::write_row(
srv_conc_enter_innodb(prebuilt->trx);
error = row_lock_table_autoinc_for_mysql(prebuilt);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
if (error != DB_SUCCESS) {
@@ -1580,7 +1640,7 @@ ha_innobase::write_row(
error = row_insert_for_mysql((byte*) record, prebuilt);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
prebuilt->trx->ignore_duplicates_in_insert = FALSE;
@@ -1775,6 +1835,8 @@ ha_innobase::update_row(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
+
+ innobase_release_stat_resources(prebuilt->trx);
}
if (prebuilt->upd_node) {
@@ -1799,7 +1861,7 @@ ha_innobase::update_row(
error = row_update_for_mysql((byte*) old_row, prebuilt);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
error = convert_error_code_to_mysql(error);
@@ -1828,6 +1890,8 @@ ha_innobase::delete_row(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
+
+ innobase_release_stat_resources(prebuilt->trx);
}
if (!prebuilt->upd_node) {
@@ -1843,7 +1907,7 @@ ha_innobase::delete_row(
error = row_update_for_mysql((byte*) record, prebuilt);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
error = convert_error_code_to_mysql(error);
@@ -1942,6 +2006,8 @@ ha_innobase::index_read(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
+
+ innobase_release_stat_resources(prebuilt->trx);
}
index = prebuilt->index;
@@ -1987,7 +2053,7 @@ ha_innobase::index_read(
ret = row_search_for_mysql((byte*) buf, mode, prebuilt, match_mode, 0);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
if (ret == DB_SUCCESS) {
error = 0;
@@ -2126,7 +2192,7 @@ ha_innobase::general_fetch(
ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
direction);
- srv_conc_exit_innodb();
+ srv_conc_exit_innodb(prebuilt->trx);
if (ret == DB_SUCCESS) {
error = 0;
@@ -2426,88 +2492,6 @@ int ha_innobase::reset(void)
return(0);
}
-/**********************************************************************
-As MySQL will execute an external lock for every new table it uses when it
-starts to process an SQL statement, we can use this function to store the
-pointer to the THD in the handle. We will also use this function to communicate
-to InnoDB that a new SQL statement has started and that we must store a
-savepoint to our transaction handle, so that we are able to roll back
-the SQL statement in case of an error. */
-
-int
-ha_innobase::external_lock(
-/*=======================*/
- THD* thd, /* in: handle to the user thread */
- int lock_type) /* in: lock type */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error = 0;
- trx_t* trx;
-
- DBUG_ENTER("ha_innobase::external_lock");
-
- update_thd(thd);
-
- trx = prebuilt->trx;
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->in_update_remember_pos = TRUE;
-
- prebuilt->read_just_key = 0;
-
- if (lock_type == F_WRLCK) {
-
- /* If this is a SELECT, then it is in UPDATE TABLE ...
- or SELECT ... FOR UPDATE */
- prebuilt->select_lock_type = LOCK_X;
- }
-
- if (lock_type != F_UNLCK) {
- if (trx->n_mysql_tables_in_use == 0) {
- trx_mark_sql_stat_end(trx);
- }
-
- thd->transaction.all.innodb_active_trans = 1;
- trx->n_mysql_tables_in_use++;
-
- if (prebuilt->select_lock_type != LOCK_NONE) {
-
- trx->mysql_n_tables_locked++;
- }
- } else {
- trx->n_mysql_tables_in_use--;
- auto_inc_counter_for_this_stat = 0;
-
- if (trx->n_mysql_tables_in_use == 0) {
-
- trx->mysql_n_tables_locked = 0;
-
- if (trx->has_search_latch) {
-
- trx_search_latch_release_if_reserved(trx);
- }
-
- if (trx->auto_inc_lock) {
-
- /* If we had reserved the auto-inc lock for
- some table in this SQL statement, we release
- it now */
-
- srv_conc_enter_innodb(trx);
- row_unlock_table_autoinc_for_mysql(trx);
- srv_conc_exit_innodb();
- }
-
- if (!(thd->options
- & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) {
-
- innobase_commit(thd, trx);
- }
- }
- }
-
- DBUG_RETURN(error);
-}
/*********************************************************************
Creates a table definition to an InnoDB database. */
@@ -2684,7 +2668,7 @@ ha_innobase::create(
/* Create the table definition in InnoDB */
- if ((error = create_table_def(trx, form, norm_name))) {
+ if (error = create_table_def(trx, form, norm_name)) {
trx_commit_for_mysql(trx);
@@ -3166,7 +3150,22 @@ ha_innobase::info(
rec_per_key = 1;
}
- table->key_info[i].rec_per_key[j]
+ /* Since the MySQL optimizer is often too
+ pessimistic in the assumption that a table
+ does not fit in the buffer pool, we
+ increase the attractiveness of indexes
+ by assuming the selectivity of any prefix
+ of an index is 1 / 100 or better.
+ (Actually, we should look at the table
+ size, and if the table is smaller than
+ the buffer pool, we should uniformly
+ increase the attractiveness of indexes,
+ regardless of the estimated selectivity.) */
+
+ if (rec_per_key > records / 100) {
+ rec_per_key = records / 100;
+ }
+ table->key_info[i].rec_per_key[j]
= rec_per_key;
}
@@ -3264,6 +3263,78 @@ ha_innobase::update_table_comment(
return(str);
}
+/**********************************************************************
+As MySQL will execute an external lock for every new table it uses when it
+starts to process an SQL statement, we can use this function to store the
+pointer to the THD in the handle. We will also use this function to communicate
+to InnoDB that a new SQL statement has started and that we must store a
+savepoint to our transaction handle, so that we are able to roll back
+the SQL statement in case of an error. */
+
+int
+ha_innobase::external_lock(
+/*=======================*/
+ THD* thd, /* in: handle to the user thread */
+ int lock_type) /* in: lock type */
+{
+ row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+ int error = 0;
+ trx_t* trx;
+
+ DBUG_ENTER("ha_innobase::external_lock");
+
+ update_thd(thd);
+
+ trx = prebuilt->trx;
+
+ prebuilt->sql_stat_start = TRUE;
+ prebuilt->in_update_remember_pos = TRUE;
+
+ prebuilt->read_just_key = 0;
+
+ if (lock_type == F_WRLCK) {
+
+ /* If this is a SELECT, then it is in UPDATE TABLE ...
+ or SELECT ... FOR UPDATE */
+ prebuilt->select_lock_type = LOCK_X;
+ }
+
+ if (lock_type != F_UNLCK) {
+ if (trx->n_mysql_tables_in_use == 0) {
+ trx_mark_sql_stat_end(trx);
+ }
+
+ thd->transaction.all.innodb_active_trans = 1;
+ trx->n_mysql_tables_in_use++;
+
+ if (prebuilt->select_lock_type != LOCK_NONE) {
+
+ trx->mysql_n_tables_locked++;
+ }
+ } else {
+ trx->n_mysql_tables_in_use--;
+ auto_inc_counter_for_this_stat = 0;
+
+ if (trx->n_mysql_tables_in_use == 0) {
+
+ trx->mysql_n_tables_locked = 0;
+
+ /* Here we release the search latch, auto_inc_lock,
+ and InnoDB thread FIFO ticket if they were reserved. */
+
+ innobase_release_stat_resources(trx);
+
+ if (!(thd->options
+ & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) {
+
+ innobase_commit(thd, trx);
+ }
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
/****************************************************************************
Handling the shared INNOBASE_SHARE structure that is needed to provide table
locking.
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index c1c9bd3ddb8..f926f303b26 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -189,6 +189,11 @@ bool innobase_flush_logs(void);
uint innobase_get_free_space(void);
int innobase_commit(THD *thd, void* trx_handle);
+int innobase_report_binlog_offset_and_commit(
+ THD* thd,
+ void* trx_handle,
+ char* log_file_name,
+ my_off_t end_offset);
int innobase_rollback(THD *thd, void* trx_handle);
int innobase_close_connection(THD *thd);
int innobase_drop_database(char *path);
diff --git a/sql/handler.cc b/sql/handler.cc
index 58f8192cf22..1e879c55b5e 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -221,6 +221,44 @@ int ha_autocommit_or_rollback(THD *thd, int error)
DBUG_RETURN(error);
}
+/* This function is called when MySQL writes the log segment of a transaction
+to the binlog. It is called when the LOCK_log mutex is reserved. Here we
+communicate to transactional table handlers whta binlog position corresponds
+to the current transaction. The handler can store it and in recovery print
+to the user, so that the user knows from what position in the binlog to
+start possible roll-forward, for example, if the crashed server was a slave
+in replication. This function also calls the commit of the table handler,
+because the order of trasnactions in the log of the table handler must be
+the same as in the binlog. */
+
+int ha_report_binlog_offset_and_commit(
+ THD *thd, /* in: user thread */
+ char *log_file_name, /* in: latest binlog file name */
+ my_off_t end_offset) /* in: the offset in the binlog file
+ up to which we wrote */
+{
+ THD_TRANS *trans;
+ int error = 0;
+
+ trans = &thd->transaction.all;
+
+#ifdef HAVE_INNOBASE_DB
+ if (trans->innobase_tid)
+ {
+ if ((error=innobase_report_binlog_offset_and_commit(thd,
+ trans->innobase_tid,
+ log_file_name,
+ end_offset)))
+ {
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), error);
+ error=1;
+ }
+ trans->innodb_active_trans=0;
+ }
+#endif
+
+ return error;
+}
int ha_commit_trans(THD *thd, THD_TRANS* trans)
{
@@ -233,7 +271,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans)
if (trans == &thd->transaction.all && mysql_bin_log.is_open() &&
my_b_tell(&thd->transaction.trans_log))
{
- mysql_bin_log.write(&thd->transaction.trans_log);
+ mysql_bin_log.write(thd, &thd->transaction.trans_log);
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;
diff --git a/sql/handler.h b/sql/handler.h
index 8d8dfeb8da4..b083465afc5 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -361,6 +361,10 @@ int ha_delete_table(enum db_type db_type, const char *path);
void ha_drop_database(char* path);
void ha_key_cache(void);
int ha_start_stmt(THD *thd);
+int ha_report_binlog_offset_and_commit(
+ THD *thd,
+ char *log_file_name,
+ my_off_t end_offset);
int ha_commit_trans(THD *thd, THD_TRANS *trans);
int ha_rollback_trans(THD *thd, THD_TRANS *trans);
int ha_autocommit_or_rollback(THD *thd, int error);
diff --git a/sql/log.cc b/sql/log.cc
index 13ebbd9a3e7..892780d3882 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -873,9 +873,12 @@ bool MYSQL_LOG::write(Log_event* event_info)
VOID(pthread_mutex_unlock(&LOCK_log));
return 0;
}
+
error=1;
- // no check for auto events flag here - this write method should
- // never be called if auto-events are enabled
+ /*
+ No check for auto events flag here - this write method should
+ never be called if auto-events are enabled
+ */
if (thd && thd->last_insert_id_used)
{
Intvar_log_event e(thd,(uchar)LAST_INSERT_ID_EVENT,thd->last_insert_id);
@@ -913,8 +916,24 @@ bool MYSQL_LOG::write(Log_event* event_info)
file == &log_file && flush_io_cache(file))
goto err;
error=0;
- should_rotate = (file == &log_file &&
- (uint)my_b_tell(file) >= max_binlog_size);
+
+ /*
+ Tell for transactional table handlers up to which position in the
+ binlog file we wrote. The table handler can store this info, and
+ after crash recovery print for the user the offset of the last
+ transactions which were recovered. Actually, we must also call
+ the table handler commit here, protected by the LOCK_log mutex,
+ because otherwise the transactions may end up in a different order
+ in the table handler log!
+ */
+
+ if (file == &log_file)
+ {
+ error = ha_report_binlog_offset_and_commit(thd, log_file_name,
+ file->pos_in_file);
+ should_rotate= (my_b_tell(file) >= (my_off_t) max_binlog_size);
+ }
+
err:
if (error)
{
@@ -948,7 +967,7 @@ uint MYSQL_LOG::next_file_id()
'cache' needs to be reinitialized after this functions returns.
*/
-bool MYSQL_LOG::write(IO_CACHE *cache)
+bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache)
{
VOID(pthread_mutex_lock(&LOCK_log));
bool error=1;
@@ -984,6 +1003,10 @@ bool MYSQL_LOG::write(IO_CACHE *cache)
sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno);
goto err;
}
+ error = ha_report_binlog_offset_and_commit(thd, log_file_name,
+ log_file.pos_in_file);
+ if (error)
+ goto err;
}
error=0;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 4e12cefa271..8e663a0cdf5 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -103,10 +103,12 @@ public:
bool write(THD *thd, const char *query, uint query_length,
time_t query_start=0);
bool write(Log_event* event_info); // binary log write
- bool write(IO_CACHE *cache);
+ bool write(THD *thd, IO_CACHE *cache);
- //v stands for vector
- //invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
+ /*
+ v stands for vector
+ invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
+ */
bool appendv(const char* buf,uint len,...);
bool append(Log_event* ev);