summaryrefslogtreecommitdiffstats
path: root/Bugzilla
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla')
-rw-r--r--Bugzilla/DB.pm64
-rw-r--r--Bugzilla/DB/Mysql.pm29
-rw-r--r--Bugzilla/Install/DB.pm4
3 files changed, 57 insertions, 40 deletions
diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm
index 22c6bbafa..095ba27a6 100644
--- a/Bugzilla/DB.pm
+++ b/Bugzilla/DB.pm
@@ -859,39 +859,51 @@ sub bz_table_list_real {
# Transaction Methods
#####################################################################
+sub bz_in_transaction {
+ return $_[0]->{private_bz_transaction_count} ? 1 : 0;
+}
+
sub bz_start_transaction {
my ($self) = @_;
- if ($self->{private_bz_in_transaction}) {
- ThrowCodeError("nested_transaction");
+ if ($self->bz_in_transaction) {
+ $self->{private_bz_transaction_count}++;
} else {
# Turn AutoCommit off and start a new transaction
$self->begin_work();
- $self->{private_bz_in_transaction} = 1;
+ # REPEATABLE READ means "We work on a snapshot of the DB that
+ # is created when we execute our first SQL statement." It's
+ # what we need in Bugzilla to be safe, for what we do.
+ # Different DBs have different defaults for their isolation
+ # level, so we just set it here manually.
+ $self->do('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
+ $self->{private_bz_transaction_count} = 1;
}
}
sub bz_commit_transaction {
my ($self) = @_;
-
- if (!$self->{private_bz_in_transaction}) {
- ThrowCodeError("not_in_transaction");
- } else {
+
+ if ($self->{private_bz_transaction_count} > 1) {
+ $self->{private_bz_transaction_count}--;
+ } elsif ($self->bz_in_transaction) {
$self->commit();
-
- $self->{private_bz_in_transaction} = 0;
+ $self->{private_bz_transaction_count} = 0;
+ } else {
+ ThrowCodeError('not_in_transaction');
}
}
sub bz_rollback_transaction {
my ($self) = @_;
- if (!$self->{private_bz_in_transaction}) {
+ # Unlike start and commit, if we rollback at any point it happens
+ # instantly, even if we're in a nested transaction.
+ if (!$self->bz_in_transaction) {
ThrowCodeError("not_in_transaction");
} else {
$self->rollback();
-
- $self->{private_bz_in_transaction} = 0;
+ $self->{private_bz_transaction_count} = 0;
}
}
@@ -928,9 +940,6 @@ sub db_new {
# above "die" condition.
$self->{RaiseError} = 1;
- # class variables
- $self->{private_bz_in_transaction} = 0;
-
bless ($self, $class);
return $self;
@@ -2212,20 +2221,33 @@ in the database.
=over
+=item C<bz_in_transaction>
+
+Returns C<1> if we are currently in the middle of an uncommitted transaction,
+C<0> otherwise.
+
=item C<bz_start_transaction>
-Starts a transaction if supported by the database being used. Returns nothing
-and takes no parameters.
+Starts a transaction.
+
+It is OK to call C<bz_start_transaction> when you are already inside of
+a transaction. However, you must call L</bz_commit_transaction> as many
+times as you called C<bz_start_transaction>, in order for your transaction
+to actually commit.
+
+Bugzilla uses C<REPEATABLE READ> transactions.
+
+Returns nothing and takes no parameters.
=item C<bz_commit_transaction>
-Ends a transaction, commiting all changes, if supported by the database
-being used. Returns nothing and takes no parameters.
+Ends a transaction, commiting all changes. Returns nothing and takes
+no parameters.
=item C<bz_rollback_transaction>
-Ends a transaction, rolling back all changes, if supported by the database
-being used. Returns nothing and takes no parameters.
+Ends a transaction, rolling back all changes. Returns nothing and takes
+no parameters.
=back
diff --git a/Bugzilla/DB/Mysql.pm b/Bugzilla/DB/Mysql.pm
index 582a2a7b8..582df3d54 100644
--- a/Bugzilla/DB/Mysql.pm
+++ b/Bugzilla/DB/Mysql.pm
@@ -207,14 +207,23 @@ sub bz_lock_tables {
ThrowCodeError("already_locked", { current => $self->{private_bz_tables_locked},
new => $list });
} else {
+ $self->bz_start_transaction();
$self->do('LOCK TABLE ' . $list);
-
$self->{private_bz_tables_locked} = $list;
}
}
sub bz_unlock_tables {
my ($self, $abort) = @_;
+
+ if ($self->bz_in_transaction) {
+ if ($abort) {
+ $self->bz_rollback_transaction();
+ }
+ else {
+ $self->bz_commit_transaction();
+ }
+ }
# Check first if there was previous matching lock
if (!$self->{private_bz_tables_locked}) {
@@ -223,28 +232,10 @@ sub bz_unlock_tables {
ThrowCodeError("no_matching_lock");
} else {
$self->do("UNLOCK TABLES");
-
$self->{private_bz_tables_locked} = "";
}
}
-# As Bugzilla currently runs on MyISAM storage, which does not support
-# transactions, these functions die when called.
-# Maybe we should just ignore these calls for now, but as we are not
-# using transactions in MySQL yet, this just hints the developers.
-sub bz_start_transaction {
- die("Attempt to start transaction on DB without transaction support");
-}
-
-sub bz_commit_transaction {
- die("Attempt to commit transaction on DB without transaction support");
-}
-
-sub bz_rollback_transaction {
- die("Attempt to rollback transaction on DB without transaction support");
-}
-
-
sub _bz_get_initial_schema {
my ($self) = @_;
return $self->_bz_build_schema_from_disk();
diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm
index 6ddca06bd..7d4939877 100644
--- a/Bugzilla/Install/DB.pm
+++ b/Bugzilla/Install/DB.pm
@@ -2205,6 +2205,9 @@ sub _migrate_email_prefs_to_new_table {
my %requestprefs = ("FlagRequestee" => EVT_FLAG_REQUESTED,
"FlagRequester" => EVT_REQUESTED_FLAG);
+ # We run the below code in a transaction to speed things up.
+ $dbh->bz_start_transaction();
+
# Select all emailflags flag strings
my $sth = $dbh->prepare("SELECT userid, emailflags FROM profiles");
$sth->execute();
@@ -2271,6 +2274,7 @@ sub _migrate_email_prefs_to_new_table {
# EVT_ATTACHMENT.
_clone_email_event(EVT_ATTACHMENT, EVT_ATTACHMENT_DATA);
+ $dbh->bz_commit_transaction();
$dbh->bz_drop_column("profiles", "emailflags");
}
}