Commit 6d4ed167 authored by unknown's avatar unknown

ha_innodb.cc, handler.cc:

  Fix the BDB crash in the previous push; to save CPU remove duplicate calls of commit in InnoDB


sql/handler.cc:
  Fix the BDB crash in the previous push; to save CPU remove duplicate calls of commit in InnoDB
sql/ha_innodb.cc:
  Fix the BDB crash in the previous push; to save CPU remove duplicate calls of commit in InnoDB
parent d6f2e8c1
...@@ -131,9 +131,11 @@ static void innobase_print_error(const char* db_errpfx, char* buffer); ...@@ -131,9 +131,11 @@ static void innobase_print_error(const char* db_errpfx, char* buffer);
/********************************************************************** /**********************************************************************
Releases possible search latch and InnoDB thread FIFO ticket. These should Releases possible search latch and InnoDB thread FIFO ticket. These should
be released at each SQL statement end. It does no harm to release these be released at each SQL statement end, and also when mysqld passes the
also in the middle of an SQL statement. */ control to the client. It does no harm to release these also in the middle
of an SQL statement. */
static static
inline
void void
innobase_release_stat_resources( innobase_release_stat_resources(
/*============================*/ /*============================*/
...@@ -896,6 +898,11 @@ innobase_commit_low( ...@@ -896,6 +898,11 @@ innobase_commit_low(
/*================*/ /*================*/
trx_t* trx) /* in: transaction handle */ trx_t* trx) /* in: transaction handle */
{ {
if (trx->conc_state == TRX_NOT_STARTED) {
return;
}
/* TODO: Guilhem should check if master_log_name, pending /* TODO: Guilhem should check if master_log_name, pending
etc. are right if the master log gets rotated! Possible bug here. etc. are right if the master log gets rotated! Possible bug here.
Comment by Heikki March 4, 2003. */ Comment by Heikki March 4, 2003. */
...@@ -910,11 +917,13 @@ innobase_commit_low( ...@@ -910,11 +917,13 @@ innobase_commit_low(
active_mi->rli.event_len + active_mi->rli.event_len +
active_mi->rli.pending)); active_mi->rli.pending));
} }
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
} }
/********************************************************************* /*********************************************************************
Commits a transaction in an InnoDB database. */ Commits a transaction in an InnoDB database or marks an SQL statement
ended. */
int int
innobase_commit( innobase_commit(
...@@ -932,29 +941,45 @@ innobase_commit( ...@@ -932,29 +941,45 @@ innobase_commit(
DBUG_ENTER("innobase_commit"); DBUG_ENTER("innobase_commit");
DBUG_PRINT("trans", ("ending transaction")); DBUG_PRINT("trans", ("ending transaction"));
/* The flag thd->transaction.all.innodb_active_trans is set to 1
in ::external_lock and ::start_stmt, and it is only set to 0 in
a commit or a rollback. If it is 0 we know there cannot be resources
to be freed and we can return immediately. */
if (thd->transaction.all.innodb_active_trans == 0) {
DBUG_RETURN(0);
}
trx = check_trx_exists(thd); trx = check_trx_exists(thd);
if (trx->auto_inc_lock) { if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle
|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
/* If we had reserved the auto-inc lock for innobase_commit_low(trx);
some table in this SQL statement, we release it now */
thd->transaction.all.innodb_active_trans = 0;
} else {
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); srv_conc_enter_innodb(trx);
row_unlock_table_autoinc_for_mysql(trx); row_unlock_table_autoinc_for_mysql(trx);
srv_conc_exit_innodb(trx); srv_conc_exit_innodb(trx);
} }
/* Store the current undo_no of the transaction so that we
know where to roll back if we have to roll back the next
SQL statement */
if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) { trx_mark_sql_stat_end(trx);
innobase_commit_low(trx);
thd->transaction.all.innodb_active_trans=0;
} }
/* Release possible statement level resources */ /* Release a possible FIFO ticket and search latch */
innobase_release_stat_resources(trx); innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
/* Tell InnoDB server that there might be work for /* Tell the InnoDB server that there might be work for utility
utility threads: */ threads: */
srv_active_wake_master_thread(); srv_active_wake_master_thread();
...@@ -1025,7 +1050,7 @@ innobase_commit_complete( ...@@ -1025,7 +1050,7 @@ innobase_commit_complete(
} }
/********************************************************************* /*********************************************************************
Rolls back a transaction in an InnoDB database. */ Rolls back a transaction or the latest SQL statement in an InnoDB database. */
int int
innobase_rollback( innobase_rollback(
...@@ -1066,11 +1091,9 @@ innobase_rollback( ...@@ -1066,11 +1091,9 @@ innobase_rollback(
srv_conc_exit_innodb(trx); srv_conc_exit_innodb(trx);
/* Release possible statement level resources */ /* Release a possible FIFO ticket and search latch */
innobase_release_stat_resources(trx); innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
DBUG_RETURN(convert_error_code_to_mysql(error, NULL)); DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
} }
...@@ -2994,6 +3017,8 @@ create_index( ...@@ -2994,6 +3017,8 @@ create_index(
KEY* key; KEY* key;
KEY_PART_INFO* key_part; KEY_PART_INFO* key_part;
ulint ind_type; ulint ind_type;
ulint col_type;
ulint prefix_len;
ulint i; ulint i;
DBUG_ENTER("create_index"); DBUG_ENTER("create_index");
...@@ -3021,10 +3046,32 @@ create_index( ...@@ -3021,10 +3046,32 @@ create_index(
for (i = 0; i < n_fields; i++) { for (i = 0; i < n_fields; i++) {
key_part = key->key_part + i; key_part = key->key_part + i;
if (key_part->length != key_part->field->pack_length()) {
prefix_len = key_part->length;
col_type = get_innobase_type_from_mysql_type(
key_part->field);
if (col_type == DATA_INT
|| col_type == DATA_FLOAT
|| col_type == DATA_DOUBLE
|| col_type == DATA_DECIMAL) {
fprintf(stderr,
"InnoDB: error: MySQL is trying to create a column prefix index field\n"
"InnoDB: on an inappropriate data type %lu. Table name %s, column name %s.\n",
col_type, table_name,
key_part->field->field_name);
prefix_len = 0;
}
} else {
prefix_len = 0;
}
/* We assume all fields should be sorted in ascending /* We assume all fields should be sorted in ascending
order, hence the '0': */ order, hence the '0': */
dict_mem_index_add_field(index, dict_mem_index_add_field(index,
(char*) key_part->field->field_name, 0); (char*) key_part->field->field_name,
0, prefix_len);
} }
error = row_create_index_for_mysql(index, trx); error = row_create_index_for_mysql(index, trx);
...@@ -3562,8 +3609,7 @@ ha_innobase::records_in_range( ...@@ -3562,8 +3609,7 @@ ha_innobase::records_in_range(
/************************************************************************* /*************************************************************************
Gives an UPPER BOUND to the number of rows in a table. This is used in Gives an UPPER BOUND to the number of rows in a table. This is used in
filesort.cc and its better if the upper bound hold. filesort.cc. */
*/
ha_rows ha_rows
ha_innobase::estimate_number_of_rows(void) ha_innobase::estimate_number_of_rows(void)
...@@ -3598,11 +3644,11 @@ ha_innobase::estimate_number_of_rows(void) ...@@ -3598,11 +3644,11 @@ ha_innobase::estimate_number_of_rows(void)
/* Calculate a minimum length for a clustered index record and from /* Calculate a minimum length for a clustered index record and from
that an upper bound for the number of rows. Since we only calculate that an upper bound for the number of rows. Since we only calculate
new statistics in row0mysql.c when a tablehas grown new statistics in row0mysql.c when a table has grown by a threshold
by a threshold factor, we must add a safety factor 2 in front factor, we must add a safety factor 2 in front of the formula below. */
of the formula below. */
estimate = 2 * local_data_file_length / dict_index_calc_min_rec_len(index); estimate = 2 * local_data_file_length /
dict_index_calc_min_rec_len(index);
prebuilt->trx->op_info = (char*)""; prebuilt->trx->op_info = (char*)"";
...@@ -3629,26 +3675,35 @@ ha_innobase::scan_time() ...@@ -3629,26 +3675,35 @@ ha_innobase::scan_time()
return((double) (prebuilt->table->stat_clustered_index_size)); return((double) (prebuilt->table->stat_clustered_index_size));
} }
/* /**********************************************************************
Calculate the time it takes to read a set of ranges through and index Calculate the time it takes to read a set of ranges through an index
This enables us to optimise reads for clustered indexes. This enables us to optimise reads for clustered indexes. */
*/
double ha_innobase::read_time(uint index, uint ranges, ha_rows rows) double
ha_innobase::read_time(
/*===================*/
/* out: estimated time measured in disk seeks */
uint index, /* in: key number */
uint ranges, /* in: how many ranges */
ha_rows rows) /* in: estimated number of rows in the ranges */
{ {
ha_rows total_rows; ha_rows total_rows;
double time_for_scan; double time_for_scan;
if (index != table->primary_key) if (index != table->primary_key)
return handler::read_time(index, ranges, rows); // Not clustered return handler::read_time(index, ranges, rows); // Not clustered
if (rows <= 2) if (rows <= 2)
return (double) rows; return (double) rows;
/*
Assume that the read is proportional to scan time for all rows + one /* Assume that the read time is proportional to the scan time for all
seek per range. rows + at most one seek per range. */
*/
time_for_scan= scan_time(); time_for_scan= scan_time();
if ((total_rows= estimate_number_of_rows()) < rows) if ((total_rows= estimate_number_of_rows()) < rows)
return time_for_scan; return time_for_scan;
return (ranges + (double) rows / (double) total_rows * time_for_scan); return (ranges + (double) rows / (double) total_rows * time_for_scan);
} }
...@@ -3992,10 +4047,10 @@ ha_innobase::reset(void) ...@@ -3992,10 +4047,10 @@ ha_innobase::reset(void)
} }
/********************************************************************** /**********************************************************************
Inside LOCK TABLES MySQL will not call external_lock() between SQL MySQL calls this function at the start of each SQL statement. Inside LOCK
statements. It will call this function at the start of each SQL statement. TABLES the ::external_lock method does not work to mark SQL statement
Note also a spacial case: if a temporary table is created inside LOCK borders. Note also a special case: if a temporary table is created inside
TABLES, MySQL has not called external_lock() at all on that table. */ LOCK TABLES, MySQL has not called external_lock() at all on that table. */
int int
ha_innobase::start_stmt( ha_innobase::start_stmt(
...@@ -4010,8 +4065,14 @@ ha_innobase::start_stmt( ...@@ -4010,8 +4065,14 @@ ha_innobase::start_stmt(
trx = prebuilt->trx; trx = prebuilt->trx;
/* Here we release the search latch and the InnoDB thread FIFO ticket
if they were reserved. They should have been released already at the
end of the previous statement, but because inside LOCK TABLES the
lock count method does not work to mark the end of a SELECT statement,
that may not be the case. We MUST release the search latch before an
INSERT, for example. */
innobase_release_stat_resources(trx); innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
&& trx->read_view) { && trx->read_view) {
...@@ -4035,6 +4096,7 @@ ha_innobase::start_stmt( ...@@ -4035,6 +4096,7 @@ ha_innobase::start_stmt(
prebuilt->select_lock_type = LOCK_X; prebuilt->select_lock_type = LOCK_X;
} }
/* Set the MySQL flag to mark that there is an active transaction */
thd->transaction.all.innodb_active_trans = 1; thd->transaction.all.innodb_active_trans = 1;
return(0); return(0);
...@@ -4098,17 +4160,20 @@ ha_innobase::external_lock( ...@@ -4098,17 +4160,20 @@ ha_innobase::external_lock(
} }
if (lock_type != F_UNLCK) { if (lock_type != F_UNLCK) {
if (trx->n_mysql_tables_in_use == 0) { /* MySQL is setting a new table lock */
trx_mark_sql_stat_end(trx);
}
/* Set the MySQL flag to mark that there is an active
transaction */
thd->transaction.all.innodb_active_trans = 1; thd->transaction.all.innodb_active_trans = 1;
trx->n_mysql_tables_in_use++; trx->n_mysql_tables_in_use++;
prebuilt->mysql_has_locked = TRUE; prebuilt->mysql_has_locked = TRUE;
if (trx->n_mysql_tables_in_use == 1) {
trx->isolation_level = innobase_map_isolation_level( trx->isolation_level = innobase_map_isolation_level(
(enum_tx_isolation) (enum_tx_isolation)
thd->variables.tx_isolation); thd->variables.tx_isolation);
}
if (trx->isolation_level == TRX_ISO_SERIALIZABLE if (trx->isolation_level == TRX_ISO_SERIALIZABLE
&& prebuilt->select_lock_type == LOCK_NONE) { && prebuilt->select_lock_type == LOCK_NONE) {
...@@ -4124,22 +4189,30 @@ ha_innobase::external_lock( ...@@ -4124,22 +4189,30 @@ ha_innobase::external_lock(
trx->mysql_n_tables_locked++; trx->mysql_n_tables_locked++;
} }
} else {
DBUG_RETURN(error);
}
/* MySQL is releasing a table lock */
trx->n_mysql_tables_in_use--; trx->n_mysql_tables_in_use--;
prebuilt->mysql_has_locked = FALSE; prebuilt->mysql_has_locked = FALSE;
auto_inc_counter_for_this_stat = 0; auto_inc_counter_for_this_stat = 0;
/* If the MySQL lock count drops to zero we know that the current SQL
statement has ended */
if (trx->n_mysql_tables_in_use == 0) { if (trx->n_mysql_tables_in_use == 0) {
trx->mysql_n_tables_locked = 0; trx->mysql_n_tables_locked = 0;
prebuilt->used_in_HANDLER = FALSE; prebuilt->used_in_HANDLER = FALSE;
/* Here we release the search latch and InnoDB if (!(thd->options
thread FIFO ticket if they were reserved. */ & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
if (thd->transaction.all.innodb_active_trans != 0) {
innobase_release_stat_resources(trx); innobase_commit(thd, trx);
}
} else {
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
&& trx->read_view) { && trx->read_view) {
...@@ -4148,13 +4221,12 @@ ha_innobase::external_lock( ...@@ -4148,13 +4221,12 @@ ha_innobase::external_lock(
read_view_close_for_mysql(trx); read_view_close_for_mysql(trx);
} }
}
if (!(thd->options /* Here we release the search latch and the InnoDB thread FIFO
& (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { ticket if they were reserved. */
innobase_commit(thd, trx); innobase_release_stat_resources(trx);
}
}
} }
DBUG_RETURN(error); DBUG_RETURN(error);
...@@ -4473,4 +4545,3 @@ ha_innobase::get_auto_increment() ...@@ -4473,4 +4545,3 @@ ha_innobase::get_auto_increment()
} }
#endif /* HAVE_INNOBASE_DB */ #endif /* HAVE_INNOBASE_DB */
...@@ -209,44 +209,26 @@ void ha_close_connection(THD* thd) ...@@ -209,44 +209,26 @@ void ha_close_connection(THD* thd)
/* /*
This is used to commit or rollback a single statement depending on the value This is used to commit or rollback a single statement depending on the value
of error. If the autocommit is on, then we will commit or rollback the whole of error. Note that if the autocommit is on, then the following call inside
transaction (= the statement). The autocommit mechanism built into handlers InnoDB will commit or rollback the whole transaction (= the statement). The
is based on counting locks, but if the user has used LOCK TABLES then that autocommit mechanism built into InnoDB is based on counting locks, but if
mechanism does not know to do the commit. the user has used LOCK TABLES then that mechanism does not know to do the
commit.
*/ */
int ha_autocommit_or_rollback(THD *thd, int error) int ha_autocommit_or_rollback(THD *thd, int error)
{ {
bool do_autocommit=FALSE;
DBUG_ENTER("ha_autocommit_or_rollback"); DBUG_ENTER("ha_autocommit_or_rollback");
#ifdef USING_TRANSACTIONS #ifdef USING_TRANSACTIONS
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
do_autocommit=TRUE; /* We can commit or rollback the whole transaction */
if (opt_using_transactions) if (opt_using_transactions)
{ {
if (!error) if (!error)
{
if (do_autocommit)
{
if (ha_commit(thd))
error=1;
}
else
{ {
if (ha_commit_stmt(thd)) if (ha_commit_stmt(thd))
error=1; error=1;
} }
}
else
{
if (do_autocommit)
(void) ha_rollback(thd);
else else
(void) ha_rollback_stmt(thd); (void) ha_rollback_stmt(thd);
}
thd->variables.tx_isolation=thd->session_tx_isolation; thd->variables.tx_isolation=thd->session_tx_isolation;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment