b15b36ded7c151aeac6fb5b16215b6fbe7d01bf4
[freeside.git] / FS / FS / Misc / Savepoint.pm
1 package FS::Misc::Savepoint;
2
3 use strict;
4 use warnings;
5
6 use Exporter;
7 use vars qw( @ISA @EXPORT @EXPORT_OK );
8 @ISA = qw( Exporter );
9 @EXPORT = qw( savepoint_create savepoint_release savepoint_rollback );
10
11 use FS::UID qw( dbh );
12 use Carp qw( croak );
13
14 =head1 NAME
15
16 FS::Misc::Savepoint - Provides methods for SQL Savepoints
17
18 =head1 SYNOPSIS
19
20   use FS::Misc::Savepoint;
21   
22   # Only valid within a transaction
23   local $FS::UID::AutoCommit = 0;
24   
25   savepoint_create( 'savepoint_label' );
26   
27   my $error_msg = do_some_things();
28   
29   if ( $error_msg ) {
30     savepoint_rollback_and_release( 'savepoint_label' );
31   } else {
32     savepoint_release( 'savepoint_label' );
33   }
34
35
36 =head1 DESCRIPTION
37
38 Provides methods for SQL Savepoints
39
40 Using a savepoint allows for a partial roll-back of SQL statements without
41 forcing a rollback of the entire enclosing transaction.
42
43 =head1 METHODS
44
45 =over 4
46
47 =item savepoint_create LABEL
48
49 =item savepoint_create { label => LABEL, dbh => DBH }
50
51 Executes SQL to create a savepoint named LABEL.
52
53 Savepoints cannot work while AutoCommit is enabled.
54
55 Savepoint labels must be valid sql identifiers.  If your choice of label
56 would not make a valid column name, it probably will not make a valid label.
57
58 Savepint labels must be unique within the transaction.
59
60 =cut
61
62 sub savepoint_create {
63   my %param = _parse_params( @_ );
64
65   $param{dbh}->do("SAVEPOINT $param{label}")
66     or die $param{dbh}->errstr;
67 }
68
69 =item savepoint_release LABEL
70
71 =item savepoint_release { label => LABEL, dbh => DBH }
72
73 Release the savepoint - preserves the SQL statements issued since the
74 savepoint was created, but does not commit the transaction.
75
76 The savepoint label is freed for future use.
77
78 =cut
79
80 sub savepoint_release {
81   my %param = _parse_params( @_ );
82
83   $param{dbh}->do("RELEASE SAVEPOINT $param{label}")
84     or die $param{dbh}->errstr;
85 }
86
87 =item savepoint_rollback LABEL
88
89 =item savepoint_rollback { label => LABEL, dbh => DBH }
90
91 Roll back the savepoint - forgets all SQL statements issues since the
92 savepoint was created, but does not commit or roll back the transaction.
93
94 The savepoint still exists.  Additional statements may be executed,
95 and savepoint_rollback called again.
96
97 =cut
98
99 sub savepoint_rollback {
100   my %param = _parse_params( @_ );
101
102   $param{dbh}->do("ROLLBACK TO SAVEPOINT $param{label}")
103     or die $param{dbh}->errstr;
104 }
105
106 =item savepoint_rollback_and_release LABEL
107
108 =item savepoint_rollback_and_release { label => LABEL, dbh => DBH }
109
110 Rollback and release the savepoint
111
112 =cut
113
114 sub savepoint_rollback_and_release {
115   savepoint_rollback( @_ );
116   savepoint_release( @_ );
117 }
118
119 =back
120
121 =head1 METHODS - Internal
122
123 =over 4
124
125 =item _parse_params
126
127 Create %params from function input
128
129 Basic savepoint label validation
130
131 Complain when trying to use savepoints without disabling AutoCommit
132
133 =cut
134
135 sub _parse_params {
136   my %param = ref $_[0] ? %{ $_[0] } : ( label => $_[0] );
137   $param{dbh} ||= dbh;
138
139   # Savepoints may be any valid SQL identifier up to 64 characters
140   $param{label} =~ /^\w+$/
141     or croak sprintf(
142       'Invalid savepont label(%s) - use only numbers, letters, _',
143       $param{label}
144     );
145
146   croak sprintf( 'Savepoint(%s) failed - AutoCommit=1', $param{label} )
147     if $FS::UID::AutoCommit;
148
149   %param;
150 }
151
152 =back
153
154 =head1 BUGS
155
156 =head1 SEE ALSO
157
158 =cut
159
160 1;