package FS::Misc::Savepoint; use strict; use warnings; use Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK ); @ISA = qw( Exporter ); @EXPORT = qw( savepoint_create savepoint_release savepoint_rollback ); use FS::UID qw( dbh ); use Carp qw( croak ); =head1 NAME FS::Misc::Savepoint - Provides methods for SQL Savepoints =head1 SYNOPSIS use FS::Misc::Savepoint; # Only valid within a transaction local $FS::UID::AutoCommit = 0; savepoint_create( 'savepoint_label' ); my $error_msg = do_some_things(); if ( $error_msg ) { savepoint_rollback_and_release( 'savepoint_label' ); } else { savepoint_release( 'savepoint_label' ); } =head1 DESCRIPTION Provides methods for SQL Savepoints Using a savepoint allows for a partial roll-back of SQL statements without forcing a rollback of the entire enclosing transaction. =head1 METHODS =over 4 =item savepoint_create LABEL =item savepoint_create { label => LABEL, dbh => DBH } Executes SQL to create a savepoint named LABEL. Savepoints cannot work while AutoCommit is enabled. Savepoint labels must be valid sql identifiers. If your choice of label would not make a valid column name, it probably will not make a valid label. Savepoint labels must be unique within the transaction. =cut sub savepoint_create { my %param = _parse_params( @_ ); $param{dbh}->do("SAVEPOINT $param{label}") or die $param{dbh}->errstr; } =item savepoint_release LABEL =item savepoint_release { label => LABEL, dbh => DBH } Release the savepoint - preserves the SQL statements issued since the savepoint was created, but does not commit the transaction. The savepoint label is freed for future use. =cut sub savepoint_release { my %param = _parse_params( @_ ); $param{dbh}->do("RELEASE SAVEPOINT $param{label}") or die $param{dbh}->errstr; } =item savepoint_rollback LABEL =item savepoint_rollback { label => LABEL, dbh => DBH } Roll back the savepoint - forgets all SQL statements issues since the savepoint was created, but does not commit or roll back the transaction. The savepoint still exists. Additional statements may be executed, and savepoint_rollback called again. =cut sub savepoint_rollback { my %param = _parse_params( @_ ); $param{dbh}->do("ROLLBACK TO SAVEPOINT $param{label}") or die $param{dbh}->errstr; } =item savepoint_rollback_and_release LABEL =item savepoint_rollback_and_release { label => LABEL, dbh => DBH } Rollback and release the savepoint =cut sub savepoint_rollback_and_release { savepoint_rollback( @_ ); savepoint_release( @_ ); } =back =head1 METHODS - Internal =over 4 =item _parse_params Create %params from function input Basic savepoint label validation Complain when trying to use savepoints without disabling AutoCommit =cut sub _parse_params { my %param = ref $_[0] ? %{ $_[0] } : ( label => $_[0] ); $param{dbh} ||= dbh; # Savepoints may be any valid SQL identifier up to 64 characters $param{label} =~ /^\w+$/ or croak sprintf( 'Invalid savepont label(%s) - use only numbers, letters, _', $param{label} ); croak sprintf( 'Savepoint(%s) failed - AutoCommit=1', $param{label} ) if $FS::UID::AutoCommit; %param; } =back =head1 BUGS =head1 SEE ALSO =cut 1;