| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  |  | 
| 2 |  |  |  |  |  |  | use warnings; | 
| 3 | 13 |  |  | 13 |  | 22996 | use strict; | 
|  | 13 |  |  |  |  | 26 |  | 
|  | 13 |  |  |  |  | 530 |  | 
| 4 | 13 |  |  | 13 |  | 70 | use base qw( Workflow::Persister ); | 
|  | 13 |  |  |  |  | 21 |  | 
|  | 13 |  |  |  |  | 369 |  | 
| 5 | 13 |  |  | 13 |  | 72 | use DateTime; | 
|  | 13 |  |  |  |  | 25 |  | 
|  | 13 |  |  |  |  | 2217 |  | 
| 6 | 13 |  |  | 13 |  | 1569 | use DateTime::Format::Strptime; | 
|  | 13 |  |  |  |  | 805801 |  | 
|  | 13 |  |  |  |  | 353 |  | 
| 7 | 13 |  |  | 13 |  | 4591 | use DBI; | 
|  | 13 |  |  |  |  | 398606 |  | 
|  | 13 |  |  |  |  | 85 |  | 
| 8 | 13 |  |  | 13 |  | 12574 | use Log::Log4perl qw( get_logger ); | 
|  | 13 |  |  |  |  | 125292 |  | 
|  | 13 |  |  |  |  | 1060 |  | 
| 9 | 13 |  |  | 13 |  | 102 | use Workflow::Exception qw( configuration_error persist_error ); | 
|  | 13 |  |  |  |  | 25 |  | 
|  | 13 |  |  |  |  | 133 |  | 
| 10 | 13 |  |  | 13 |  | 974 | use Workflow::History; | 
|  | 13 |  |  |  |  | 132 |  | 
|  | 13 |  |  |  |  | 678 |  | 
| 11 | 13 |  |  | 13 |  | 924 | use Workflow::Persister::RandomId; | 
|  | 13 |  |  |  |  | 27 |  | 
|  | 13 |  |  |  |  | 185 |  | 
| 12 | 13 |  |  | 13 |  | 4012 | use Workflow::Persister::DBI::AutoGeneratedId; | 
|  | 13 |  |  |  |  | 38 |  | 
|  | 13 |  |  |  |  | 66 |  | 
| 13 | 13 |  |  | 13 |  | 4948 | use Workflow::Persister::DBI::SequenceId; | 
|  | 13 |  |  |  |  | 30 |  | 
|  | 13 |  |  |  |  | 54 |  | 
| 14 | 13 |  |  | 13 |  | 5031 | use Carp qw(croak); | 
|  | 13 |  |  |  |  | 131 |  | 
|  | 13 |  |  |  |  | 296 |  | 
| 15 | 13 |  |  | 13 |  | 555 | use English qw( -no_match_vars ); | 
|  | 13 |  |  |  |  | 37 |  | 
|  | 13 |  |  |  |  | 621 |  | 
| 16 | 13 |  |  | 13 |  | 68 | use Readonly; | 
|  | 13 |  |  |  |  | 214 |  | 
|  | 13 |  |  |  |  | 72 |  | 
| 17 | 13 |  |  | 13 |  | 10198 |  | 
|  | 13 |  |  |  |  | 45658 |  | 
|  | 13 |  |  |  |  | 31190 |  | 
| 18 |  |  |  |  |  |  | Readonly::Scalar my $TRUE => 1; | 
| 19 |  |  |  |  |  |  | Readonly::Scalar my $FALSE => 0; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | $Workflow::Persister::DBI::VERSION = '1.60'; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | my @FIELDS = qw( _wf_fields _hist_fields handle dsn user password driver | 
| 24 |  |  |  |  |  |  | workflow_table history_table date_format parser autocommit); | 
| 25 |  |  |  |  |  |  | __PACKAGE__->mk_accessors(@FIELDS); | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | my ( $self, $params ) = @_; | 
| 29 |  |  |  |  |  |  | $self->SUPER::init($params); | 
| 30 | 14 |  |  | 14 | 1 | 33 |  | 
| 31 | 14 |  |  |  |  | 99 | # Default to old date format if not provided so we don't break old configurations. | 
| 32 |  |  |  |  |  |  | $self->date_format('%Y-%m-%d %H:%M'); | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 14 |  |  |  |  | 9367 | # Default to autocommit on for backward compatibility. | 
| 35 |  |  |  |  |  |  | $self->autocommit($TRUE); | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 14 |  |  |  |  | 197 | # Load user-provided values from config. | 
| 38 |  |  |  |  |  |  | for (qw( dsn user password date_format autocommit )) { | 
| 39 |  |  |  |  |  |  | $self->$_( $params->{$_} ) if ( defined $params->{$_} ); | 
| 40 | 14 |  |  |  |  | 136 | } | 
| 41 | 70 | 100 |  |  |  | 487 | $self->handle($self->create_handle); | 
| 42 |  |  |  |  |  |  | my $driver | 
| 43 | 14 |  |  |  |  | 53 | = $self->handle ? $self->handle->{Driver}->{Name} : ($params->{driver} || ''); | 
| 44 |  |  |  |  |  |  | $self->log->debug( "Pulled driver '$driver' from DBI DSN" ); | 
| 45 | 14 | 100 | 50 |  |  | 242 | $self->driver($driver); | 
| 46 | 14 |  |  |  |  | 757 | $self->assign_generators( $params, $driver ); | 
| 47 | 14 |  |  |  |  | 3311 | $self->log->info( | 
| 48 | 14 |  |  |  |  | 191 | "Assigned workflow generator '", | 
| 49 | 14 |  |  |  |  | 138 | ref( $self->workflow_id_generator ), | 
| 50 |  |  |  |  |  |  | "'; ", | 
| 51 |  |  |  |  |  |  | "history generator '", | 
| 52 |  |  |  |  |  |  | ref( $self->history_id_generator ), | 
| 53 |  |  |  |  |  |  | "'" | 
| 54 |  |  |  |  |  |  | ); | 
| 55 |  |  |  |  |  |  | $self->assign_tables($params); | 
| 56 |  |  |  |  |  |  | $self->log->info( | 
| 57 | 14 |  |  |  |  | 3645 | "Assigned workflow table '", | 
| 58 | 14 |  |  |  |  | 171 | $self->workflow_table, "'; ", "history table '", | 
| 59 |  |  |  |  |  |  | $self->history_table, "'" | 
| 60 |  |  |  |  |  |  | ); | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | my $parser | 
| 63 |  |  |  |  |  |  | = DateTime::Format::Strptime->new( pattern => $self->date_format ); | 
| 64 | 14 |  |  |  |  | 3658 | $self->parser($parser); | 
| 65 |  |  |  |  |  |  | } | 
| 66 | 14 |  |  |  |  | 22316 |  | 
| 67 |  |  |  |  |  |  | my ($self, $params) = @_; | 
| 68 |  |  |  |  |  |  | unless ( $self->dsn ) { | 
| 69 |  |  |  |  |  |  | configuration_error "DBI persister configuration must include ", | 
| 70 | 13 |  |  | 13 | 1 | 28 | "key 'dsn' which maps to the first paramter ", | 
| 71 | 13 | 50 |  |  |  | 57 | "in the DBI 'connect()' call."; | 
| 72 | 0 |  |  |  |  | 0 | } | 
| 73 |  |  |  |  |  |  | my $dbh = eval { | 
| 74 |  |  |  |  |  |  | DBI->connect( $self->dsn, $self->user, $self->password ) | 
| 75 |  |  |  |  |  |  | || croak "Cannot connect to database: $DBI::errstr"; | 
| 76 | 13 |  |  |  |  | 164 | }; | 
| 77 | 13 | 50 |  |  |  | 61 | if ($EVAL_ERROR) { | 
| 78 |  |  |  |  |  |  | persist_error $EVAL_ERROR; | 
| 79 |  |  |  |  |  |  | } | 
| 80 | 13 | 50 |  |  |  | 162730 | $dbh->{RaiseError} = $TRUE; | 
| 81 | 0 |  |  |  |  | 0 | $dbh->{PrintError} = $FALSE; | 
| 82 |  |  |  |  |  |  | $dbh->{ChopBlanks} = $TRUE; | 
| 83 | 13 |  |  |  |  | 69 | $dbh->{AutoCommit} = $self->autocommit(); | 
| 84 | 13 |  |  |  |  | 243 | $self->log->debug( "Connected to database '", | 
| 85 | 13 |  |  |  |  | 204 | $self->dsn, "' and ", "assigned to persister ok" ); | 
| 86 | 13 |  |  |  |  | 249 |  | 
| 87 | 13 |  |  |  |  | 402 | return $dbh; | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 13 |  |  |  |  | 4415 | my ( $self, $params, $driver ) = @_; | 
| 91 |  |  |  |  |  |  | $self->SUPER::assign_generators($params); | 
| 92 |  |  |  |  |  |  | return | 
| 93 |  |  |  |  |  |  | if ($self->workflow_id_generator | 
| 94 | 14 |  |  | 14 | 1 | 35 | and $self->history_id_generator ); | 
| 95 | 14 |  |  |  |  | 137 |  | 
| 96 |  |  |  |  |  |  | my ( $wf_gen, $history_gen ); | 
| 97 | 14 | 50 | 33 |  |  | 74 | if ( $driver eq 'Pg' ) { | 
| 98 |  |  |  |  |  |  | $self->log->debug("Assigning ID generators for PostgreSQL"); | 
| 99 |  |  |  |  |  |  | ( $wf_gen, $history_gen ) = $self->init_postgres_generators($params); | 
| 100 | 14 |  |  |  |  | 146 | } elsif ( $driver eq 'Oracle' ) { | 
| 101 | 14 | 100 |  |  |  | 132 | $self->log->debug("Assigning ID generators for Oracle"); | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 102 | 1 |  |  |  |  | 3 | ( $wf_gen, $history_gen ) = $self->init_oracle_generators($params); | 
| 103 | 1 |  |  |  |  | 286 | } elsif ( $driver eq 'mysql' ) { | 
| 104 |  |  |  |  |  |  | $self->log->debug("Assigning ID generators for MySQL"); | 
| 105 | 0 |  |  |  |  | 0 | ( $wf_gen, $history_gen ) = $self->init_mysql_generators($params); | 
| 106 | 0 |  |  |  |  | 0 | } elsif ( $driver eq 'SQLite' ) { | 
| 107 |  |  |  |  |  |  | $self->log->debug("Assigning ID generators for SQLite"); | 
| 108 | 0 |  |  |  |  | 0 | ( $wf_gen, $history_gen ) = $self->init_sqlite_generators($params); | 
| 109 | 0 |  |  |  |  | 0 | } else { | 
| 110 |  |  |  |  |  |  | $self->log->debug("Assigning random ID generators"); | 
| 111 | 1 |  |  |  |  | 6 | ( $wf_gen, $history_gen ) = $self->init_random_generators($params); | 
| 112 | 1 |  |  |  |  | 295 | } | 
| 113 |  |  |  |  |  |  | $self->workflow_id_generator($wf_gen); | 
| 114 | 12 |  |  |  |  | 48 | $self->history_id_generator($history_gen); | 
| 115 | 12 |  |  |  |  | 2814 | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 14 |  |  |  |  | 56 | my ( $self, $params ) = @_; | 
| 118 | 14 |  |  |  |  | 190 | my $sequence_select = q{SELECT NEXTVAL( '%s' )}; | 
| 119 |  |  |  |  |  |  | $params->{workflow_sequence} ||= 'workflow_seq'; | 
| 120 |  |  |  |  |  |  | $params->{history_sequence}  ||= 'workflow_history_seq'; | 
| 121 |  |  |  |  |  |  | return ( | 
| 122 | 1 |  |  | 1 | 1 | 2 | Workflow::Persister::DBI::SequenceId->new( | 
| 123 | 1 |  |  |  |  | 2 | {   sequence_name   => $params->{workflow_sequence}, | 
| 124 | 1 |  | 50 |  |  | 6 | sequence_select => $sequence_select | 
| 125 | 1 |  | 50 |  |  | 4 | } | 
| 126 |  |  |  |  |  |  | ), | 
| 127 |  |  |  |  |  |  | Workflow::Persister::DBI::SequenceId->new( | 
| 128 |  |  |  |  |  |  | {   sequence_name   => $params->{history_sequence}, | 
| 129 |  |  |  |  |  |  | sequence_select => $sequence_select | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | ) | 
| 132 |  |  |  |  |  |  | ); | 
| 133 |  |  |  |  |  |  | } | 
| 134 | 1 |  |  |  |  | 12 |  | 
| 135 |  |  |  |  |  |  | my ( $self, $params ) = @_; | 
| 136 |  |  |  |  |  |  | my $sequence_select = q{SELECT %s.NEXTVAL from dual}; | 
| 137 |  |  |  |  |  |  | $params->{workflow_sequence} ||= 'workflow_seq'; | 
| 138 |  |  |  |  |  |  | $params->{history_sequence}  ||= 'workflow_history_seq'; | 
| 139 |  |  |  |  |  |  | return ( | 
| 140 |  |  |  |  |  |  | Workflow::Persister::DBI::SequenceId->new( | 
| 141 | 0 |  |  | 0 | 1 | 0 | {   sequence_name   => $params->{workflow_sequence}, | 
| 142 | 0 |  |  |  |  | 0 | sequence_select => $sequence_select | 
| 143 | 0 |  | 0 |  |  | 0 | } | 
| 144 | 0 |  | 0 |  |  | 0 | ), | 
| 145 |  |  |  |  |  |  | Workflow::Persister::DBI::SequenceId->new( | 
| 146 |  |  |  |  |  |  | {   sequence_name   => $params->{history_sequence}, | 
| 147 |  |  |  |  |  |  | sequence_select => $sequence_select | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | ) | 
| 150 |  |  |  |  |  |  | ); | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 0 |  |  |  |  | 0 | my ( $self, $params ) = @_; | 
| 154 |  |  |  |  |  |  | my $generator = Workflow::Persister::DBI::AutoGeneratedId->new( | 
| 155 |  |  |  |  |  |  | {   from_handle     => 'database', | 
| 156 |  |  |  |  |  |  | handle_property => 'mysql_insertid', | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | ); | 
| 159 |  |  |  |  |  |  | return ( $generator, $generator ); | 
| 160 | 0 |  |  | 0 | 1 | 0 | } | 
| 161 | 0 |  |  |  |  | 0 |  | 
| 162 |  |  |  |  |  |  | my ( $self, $params ) = @_; | 
| 163 |  |  |  |  |  |  | my $generator = Workflow::Persister::DBI::AutoGeneratedId->new( | 
| 164 |  |  |  |  |  |  | { func_property => 'last_insert_rowid' } ); | 
| 165 |  |  |  |  |  |  | return ( $generator, $generator ); | 
| 166 | 0 |  |  |  |  | 0 | } | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | my ( $self, $params ) = @_; | 
| 169 |  |  |  |  |  |  | my $wf_table   = $params->{workflow_table} || 'workflow'; | 
| 170 | 1 |  |  | 1 | 1 | 4 | my $hist_table = $params->{history_table}  || 'workflow_history'; | 
| 171 | 1 |  |  |  |  | 16 | $self->workflow_table($wf_table); | 
| 172 |  |  |  |  |  |  | $self->history_table($hist_table); | 
| 173 | 1 |  |  |  |  | 5 | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | ######################################## | 
| 176 |  |  |  |  |  |  | # PERSISTENCE IMPLEMENTATION | 
| 177 | 14 |  |  | 14 | 1 | 39 |  | 
| 178 | 14 |  | 50 |  |  | 116 | my ( $self, $wf ) = @_; | 
| 179 | 14 |  | 50 |  |  | 68 |  | 
| 180 | 14 |  |  |  |  | 55 | $self->_init_fields(); | 
| 181 | 14 |  |  |  |  | 178 | my @wf_fields = @{ $self->_wf_fields }; | 
| 182 |  |  |  |  |  |  | my @fields    = @wf_fields[ 1, 2, 3 ]; | 
| 183 |  |  |  |  |  |  | my @values    = ( | 
| 184 |  |  |  |  |  |  | $wf->type, | 
| 185 |  |  |  |  |  |  | $wf->state, | 
| 186 |  |  |  |  |  |  | DateTime->now( time_zone => $wf->time_zone() ) | 
| 187 |  |  |  |  |  |  | ->strftime( $self->date_format() ), | 
| 188 | 19 |  |  | 19 | 1 | 53 | ); | 
| 189 |  |  |  |  |  |  | my $dbh = $self->handle; | 
| 190 | 19 |  |  |  |  | 95 |  | 
| 191 | 19 |  |  |  |  | 536 | my $id = $self->workflow_id_generator->pre_fetch_id($dbh); | 
|  | 19 |  |  |  |  | 53 |  | 
| 192 | 19 |  |  |  |  | 217 | if ($id) { | 
| 193 | 19 |  |  |  |  | 72 | push @fields, $wf_fields[0]; | 
| 194 |  |  |  |  |  |  | push @values, $id; | 
| 195 |  |  |  |  |  |  | $self->log->debug("Got ID from pre_fetch_id: $id"); | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  | my $sql = 'INSERT INTO %s ( %s ) VALUES ( %s )'; | 
| 198 |  |  |  |  |  |  |  | 
| 199 | 19 |  |  |  |  | 11625 | $sql = sprintf $sql, | 
| 200 |  |  |  |  |  |  | $self->handle->quote_identifier( $self->workflow_table ), | 
| 201 | 19 |  |  |  |  | 251 | join( ', ', @fields ), | 
| 202 | 19 | 100 |  |  |  | 87 | join( ', ', map {'?'} @values ); | 
| 203 | 18 |  |  |  |  | 50 |  | 
| 204 | 18 |  |  |  |  | 38 | if ( $self->log->is_debug ) { | 
| 205 | 18 |  |  |  |  | 73 | $self->log->debug("Will use SQL: $sql"); | 
| 206 |  |  |  |  |  |  | $self->log->debug( "Will use parameters: ", join ', ', @values ); | 
| 207 | 19 |  |  |  |  | 4790 | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | my ($sth); | 
| 210 |  |  |  |  |  |  | eval { | 
| 211 |  |  |  |  |  |  | $sth = $dbh->prepare($sql); | 
| 212 | 19 |  |  |  |  | 71 | $sth->execute(@values); | 
|  | 75 |  |  |  |  | 1089 |  | 
| 213 |  |  |  |  |  |  | }; | 
| 214 | 19 | 100 |  |  |  | 77 | if ($EVAL_ERROR) { | 
| 215 | 16 |  |  |  |  | 144 | persist_error "Failed to create workflow: $EVAL_ERROR"; | 
| 216 | 16 |  |  |  |  | 4948 | } | 
| 217 |  |  |  |  |  |  | unless ($id) { | 
| 218 |  |  |  |  |  |  | $id = $self->workflow_id_generator->post_fetch_id( $dbh, $sth ); | 
| 219 | 19 |  |  |  |  | 4894 | unless ($id) { | 
| 220 | 19 |  |  |  |  | 48 | persist_error "No ID found using generator '", | 
| 221 | 19 |  |  |  |  | 178 | ref( $self->workflow_id_generator ), "'"; | 
| 222 | 19 |  |  |  |  | 4101 | } | 
| 223 |  |  |  |  |  |  | } | 
| 224 | 19 | 50 |  |  |  | 3485 | $sth->finish; | 
| 225 | 0 |  |  |  |  | 0 |  | 
| 226 |  |  |  |  |  |  | $wf->id($id); | 
| 227 | 19 | 100 |  |  |  | 70 | return $id; | 
| 228 | 1 |  |  |  |  | 19 | } | 
| 229 | 1 | 50 |  |  |  | 4 |  | 
| 230 | 0 |  |  |  |  | 0 | my ( $self, $wf_id ) = @_; | 
| 231 |  |  |  |  |  |  | $self->_init_fields(); | 
| 232 |  |  |  |  |  |  | my $sql = q{SELECT %s, %s FROM %s WHERE %s = ?}; | 
| 233 |  |  |  |  |  |  | my @wf_fields = @{ $self->_wf_fields }; | 
| 234 | 19 |  |  |  |  | 122 | $sql = sprintf $sql, | 
| 235 |  |  |  |  |  |  | $wf_fields[2], $wf_fields[3], | 
| 236 | 19 |  |  |  |  | 501 | $self->handle->quote_identifier( $self->workflow_table ), | 
| 237 | 19 |  |  |  |  | 177 | $wf_fields[0]; | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | if ( $self->log->is_debug ) { | 
| 240 |  |  |  |  |  |  | $self->log->debug("Will use SQL: $sql"); | 
| 241 | 6 |  |  | 6 | 1 | 13 | $self->log->debug("Will use parameters: $wf_id"); | 
| 242 | 6 |  |  |  |  | 18 | } | 
| 243 | 6 |  |  |  |  | 51 |  | 
| 244 | 6 |  |  |  |  | 9 | my ($sth); | 
|  | 6 |  |  |  |  | 34 |  | 
| 245 | 6 |  |  |  |  | 61 | eval { | 
| 246 |  |  |  |  |  |  | $sth = $self->handle->prepare($sql); | 
| 247 |  |  |  |  |  |  | $sth->execute($wf_id); | 
| 248 |  |  |  |  |  |  | }; | 
| 249 |  |  |  |  |  |  | if ($EVAL_ERROR) { | 
| 250 | 6 | 50 |  |  |  | 304 | persist_error "Cannot fetch workflow: $EVAL_ERROR"; | 
| 251 | 6 |  |  |  |  | 52 | } | 
| 252 | 6 |  |  |  |  | 2099 | my $row = $sth->fetchrow_arrayref; | 
| 253 |  |  |  |  |  |  | return unless ($row); | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 6 |  |  |  |  | 1736 | return { | 
| 256 | 6 |  |  |  |  | 14 | state       => $row->[0], | 
| 257 | 6 |  |  |  |  | 18 | last_update => $self->parser->parse_datetime( $row->[1] ), | 
| 258 | 6 |  |  |  |  | 1069 | }; | 
| 259 |  |  |  |  |  |  | } | 
| 260 | 6 | 50 |  |  |  | 529 |  | 
| 261 | 0 |  |  |  |  | 0 | my ( $self, $wf ) = @_; | 
| 262 |  |  |  |  |  |  | $self->_init_fields(); | 
| 263 | 6 |  |  |  |  | 91 | my $sql = q{UPDATE %s SET %s = ?, %s = ? WHERE %s = ?}; | 
| 264 | 6 | 100 |  |  |  | 271 | my @wf_fields = @{ $self->_wf_fields }; | 
| 265 |  |  |  |  |  |  | $sql          = sprintf $sql, | 
| 266 |  |  |  |  |  |  | $self->handle->quote_identifier( $self->workflow_table ), | 
| 267 | 4 |  |  |  |  | 18 | $wf_fields[2], $wf_fields[3], $wf_fields[0]; | 
| 268 |  |  |  |  |  |  | my $update_date = DateTime->now( time_zone => $wf->time_zone() ) | 
| 269 |  |  |  |  |  |  | ->strftime( $self->date_format() ); | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | if ( $self->log->is_debug ) { | 
| 272 |  |  |  |  |  |  | $self->log->debug("Will use SQL: $sql"); | 
| 273 | 52 |  |  | 52 | 1 | 117 | $self->log->debug( "Will use parameters: ", | 
| 274 | 52 |  |  |  |  | 200 | join ', ', $wf->state, $update_date, $wf->id ); | 
| 275 | 52 |  |  |  |  | 504 | } | 
| 276 | 52 |  |  |  |  | 90 |  | 
|  | 52 |  |  |  |  | 106 |  | 
| 277 | 52 |  |  |  |  | 614 | my ($sth); | 
| 278 |  |  |  |  |  |  | eval { | 
| 279 |  |  |  |  |  |  | $sth = $self->handle->prepare($sql); | 
| 280 | 52 |  |  |  |  | 3200 | $sth->execute( $wf->state, $update_date, $wf->id ); | 
| 281 |  |  |  |  |  |  | }; | 
| 282 |  |  |  |  |  |  | if ($EVAL_ERROR) { | 
| 283 | 52 | 100 |  |  |  | 26362 | persist_error $EVAL_ERROR; | 
| 284 | 18 |  |  |  |  | 169 | } | 
| 285 | 18 |  |  |  |  | 6302 | $self->log->info( "Workflow ", $wf->id, " updated ok" ); | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | my ( $self, $wf, @history ) = @_; | 
| 289 | 52 |  |  |  |  | 5885 | $self->_init_fields(); | 
| 290 | 52 |  |  |  |  | 92 |  | 
| 291 | 52 |  |  |  |  | 153 | my $dbh       = $self->handle; | 
| 292 | 52 |  |  |  |  | 9064 | my $generator = $self->history_id_generator; | 
| 293 |  |  |  |  |  |  | foreach my $h (@history) { | 
| 294 | 52 | 50 |  |  |  | 10019 | next if ( $h->is_saved ); | 
| 295 | 0 |  |  |  |  | 0 | my $id     = $generator->pre_fetch_id($dbh); | 
| 296 |  |  |  |  |  |  | my @hist_fields = @{ $self->_hist_fields }; | 
| 297 | 52 |  |  |  |  | 189 | my @fields      = @hist_fields[ 1 .. 6 ]; | 
| 298 |  |  |  |  |  |  | my @values      = ( | 
| 299 |  |  |  |  |  |  | $wf->id, $h->action, $h->description, $h->state, $h->user, | 
| 300 |  |  |  |  |  |  | $h->date->strftime( $self->date_format() ), | 
| 301 | 71 |  |  | 71 | 1 | 195 | ); | 
| 302 | 71 |  |  |  |  | 220 | if ($id) { | 
| 303 |  |  |  |  |  |  | push @fields, $hist_fields[0]; | 
| 304 | 71 |  |  |  |  | 808 | push @values, $id; | 
| 305 | 71 |  |  |  |  | 694 | } | 
| 306 | 71 |  |  |  |  | 625 | my $sql = 'INSERT INTO %s ( %s ) VALUES ( %s )'; | 
| 307 | 27 | 50 |  |  |  | 103 |  | 
| 308 | 27 |  |  |  |  | 117 | $sql = sprintf $sql, $dbh->quote_identifier( $self->history_table ), | 
| 309 | 27 |  |  |  |  | 75 | join( ', ', @fields ), join( ', ', map {'?'} @values ); | 
|  | 27 |  |  |  |  | 75 |  | 
| 310 | 27 |  |  |  |  | 323 | if ( $self->log->is_debug ) { | 
| 311 | 27 |  |  |  |  | 84 | $self->log->debug("Will use SQL: $sql"); | 
| 312 |  |  |  |  |  |  | $self->log->debug( "Will use parameters: ", join ', ', @values ); | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 27 | 100 |  |  |  | 3605 | my ($sth); | 
| 316 | 25 |  |  |  |  | 59 | eval { | 
| 317 | 25 |  |  |  |  | 48 | $sth = $dbh->prepare($sql); | 
| 318 |  |  |  |  |  |  | $sth->execute(@values); | 
| 319 | 27 |  |  |  |  | 70 | }; | 
| 320 |  |  |  |  |  |  | if ($EVAL_ERROR) { | 
| 321 |  |  |  |  |  |  | persist_error $EVAL_ERROR; | 
| 322 | 27 |  |  |  |  | 123 | } | 
|  | 187 |  |  |  |  | 1370 |  | 
| 323 | 27 | 100 |  |  |  | 111 | unless ($id) { | 
| 324 | 24 |  |  |  |  | 217 | $id = $self->history_id_generator->post_fetch_id( $dbh, $sth ); | 
| 325 | 24 |  |  |  |  | 7962 | unless ($id) { | 
| 326 |  |  |  |  |  |  | persist_error "No ID found using generator '", | 
| 327 |  |  |  |  |  |  | ref( $self->history_id_generator ), "'"; | 
| 328 | 27 |  |  |  |  | 6952 | } | 
| 329 | 27 |  |  |  |  | 62 | } | 
| 330 | 27 |  |  |  |  | 272 | $h->id($id); | 
| 331 | 27 |  |  |  |  | 3851 | $h->set_saved(); | 
| 332 |  |  |  |  |  |  | $self->log->info( "Workflow history entry ", $id, " created ok" ); | 
| 333 | 27 | 50 |  |  |  | 4132 | } | 
| 334 | 0 |  |  |  |  | 0 | return @history; | 
| 335 |  |  |  |  |  |  | } | 
| 336 | 27 | 100 |  |  |  | 105 |  | 
| 337 | 2 |  |  |  |  | 11 | my ( $self, $wf ) = @_; | 
| 338 | 2 | 50 |  |  |  | 7 | $self->_init_fields(); | 
| 339 | 0 |  |  |  |  | 0 |  | 
| 340 |  |  |  |  |  |  | my $sql = qq{SELECT %s FROM %s WHERE %s = ? ORDER BY %s DESC}; | 
| 341 |  |  |  |  |  |  | my @hist_fields    = @{ $self->_hist_fields }; | 
| 342 |  |  |  |  |  |  | my $history_fields = join ', ', @hist_fields; | 
| 343 | 27 |  |  |  |  | 145 | $sql = sprintf $sql, $history_fields, | 
| 344 | 27 |  |  |  |  | 399 | $self->handle->quote_identifier($self->history_table), | 
| 345 | 27 |  |  |  |  | 90 | $hist_fields[1], $hist_fields[6]; | 
| 346 |  |  |  |  |  |  |  | 
| 347 | 71 |  |  |  |  | 7814 | if ( $self->log->is_debug ) { | 
| 348 |  |  |  |  |  |  | $self->log->debug("Will use SQL: $sql"); | 
| 349 |  |  |  |  |  |  | $self->log->debug( "Will use parameters: ", $wf->id ); | 
| 350 |  |  |  |  |  |  | } | 
| 351 | 2 |  |  | 2 | 1 | 5 |  | 
| 352 | 2 |  |  |  |  | 5 | my ($sth); | 
| 353 |  |  |  |  |  |  | eval { | 
| 354 | 2 |  |  |  |  | 19 | $sth = $self->handle->prepare($sql); | 
| 355 | 2 |  |  |  |  | 5 | $sth->execute( $wf->id ); | 
|  | 2 |  |  |  |  | 5 |  | 
| 356 | 2 |  |  |  |  | 22 | }; | 
| 357 | 2 |  |  |  |  | 4 | if ($EVAL_ERROR) { | 
| 358 |  |  |  |  |  |  | $self->log->error("Caught error fetching workflow history: $EVAL_ERROR"); | 
| 359 |  |  |  |  |  |  | persist_error $EVAL_ERROR; | 
| 360 |  |  |  |  |  |  | } | 
| 361 | 2 | 50 |  |  |  | 90 | $self->log->debug("Prepared and executed ok"); | 
| 362 | 2 |  |  |  |  | 21 |  | 
| 363 | 2 |  |  |  |  | 566 | my @history = (); | 
| 364 |  |  |  |  |  |  | while ( my $row = $sth->fetchrow_arrayref ) { | 
| 365 |  |  |  |  |  |  | $self->log->debug("Fetched history object '$row->[0]'"); | 
| 366 | 2 |  |  |  |  | 583 |  | 
| 367 | 2 |  |  |  |  | 3 | my $hist = Workflow::History->new({ | 
| 368 | 2 |  |  |  |  | 7 | id          => $row->[0], | 
| 369 | 2 |  |  |  |  | 362 | workflow_id => $row->[1], | 
| 370 |  |  |  |  |  |  | action      => $row->[2], | 
| 371 | 2 | 50 |  |  |  | 216 | description => $row->[3], | 
| 372 | 0 |  |  |  |  | 0 | state       => $row->[4], | 
| 373 | 0 |  |  |  |  | 0 | user        => $row->[5], | 
| 374 |  |  |  |  |  |  | date        => $self->parser->parse_datetime( $row->[6] ), | 
| 375 | 2 |  |  |  |  | 5 | }); | 
| 376 |  |  |  |  |  |  | $hist->set_saved(); | 
| 377 | 2 |  |  |  |  | 637 | push @history, $hist; | 
| 378 | 2 |  |  |  |  | 14 | } | 
| 379 | 2 |  |  |  |  | 130 | $sth->finish; | 
| 380 |  |  |  |  |  |  | return @history; | 
| 381 | 2 |  |  |  |  | 562 | } | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | my ( $self, $wf ) = @_; | 
| 384 |  |  |  |  |  |  | if ( not $self->autocommit() ) { | 
| 385 |  |  |  |  |  |  | eval { $self->handle->commit(); }; | 
| 386 |  |  |  |  |  |  | if ($EVAL_ERROR) { | 
| 387 |  |  |  |  |  |  | $self->log->error("Caught error committing transaction: $EVAL_ERROR"); | 
| 388 |  |  |  |  |  |  | persist_error $EVAL_ERROR; | 
| 389 |  |  |  |  |  |  | } | 
| 390 | 2 |  |  |  |  | 19 | } | 
| 391 | 2 |  |  |  |  | 10 | } | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 2 |  |  |  |  | 65 | my ( $self, $wf ) = @_; | 
| 394 | 2 |  |  |  |  | 39 | if ( not $self->autocommit() ) { | 
| 395 |  |  |  |  |  |  | eval { $self->handle->rollback(); }; | 
| 396 |  |  |  |  |  |  | if ($EVAL_ERROR) { | 
| 397 |  |  |  |  |  |  | $self->log->error("Caught error rolling back transaction: $EVAL_ERROR"); | 
| 398 | 70 |  |  | 70 | 1 | 173 | persist_error $EVAL_ERROR; | 
| 399 | 70 | 100 |  |  |  | 204 | } | 
| 400 | 2 |  |  |  |  | 24 | } | 
|  | 2 |  |  |  |  | 8 |  | 
| 401 | 2 | 50 |  |  |  | 26449 | } | 
| 402 | 0 |  |  |  |  | 0 |  | 
| 403 | 0 |  |  |  |  | 0 | ########## | 
| 404 |  |  |  |  |  |  | # FIELDS | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | # Allow subclasses to override the fieldnames | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | my ($self) = @_; | 
| 409 | 0 |  |  | 0 | 1 | 0 | unless ( $self->_wf_fields ) { | 
| 410 | 0 | 0 |  |  |  | 0 | $self->_wf_fields( | 
| 411 | 0 |  |  |  |  | 0 | [ | 
|  | 0 |  |  |  |  | 0 |  | 
| 412 | 0 | 0 |  |  |  | 0 | map { | 
| 413 | 0 |  |  |  |  | 0 | $self->handle->quote_identifier($_) | 
| 414 | 0 |  |  |  |  | 0 | } $self->get_workflow_fields() | 
| 415 |  |  |  |  |  |  | ]); | 
| 416 |  |  |  |  |  |  | } | 
| 417 |  |  |  |  |  |  | unless ( $self->_hist_fields ) { | 
| 418 |  |  |  |  |  |  | $self->_hist_fields( | 
| 419 |  |  |  |  |  |  | [ | 
| 420 |  |  |  |  |  |  | map { | 
| 421 |  |  |  |  |  |  | $self->handle->quote_identifier($_) | 
| 422 |  |  |  |  |  |  | } $self->get_history_fields() | 
| 423 |  |  |  |  |  |  | ]); | 
| 424 |  |  |  |  |  |  | } | 
| 425 | 150 |  |  | 150 |  | 249 | } | 
| 426 | 150 | 100 |  |  |  | 453 |  | 
| 427 |  |  |  |  |  |  | return qw( workflow_id type state last_update ); | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  |  | 
| 430 | 13 |  |  |  |  | 193 | return qw( workflow_hist_id workflow_id | 
|  | 52 |  |  |  |  | 4006 |  | 
| 431 |  |  |  |  |  |  | action description state | 
| 432 |  |  |  |  |  |  | workflow_user history_date ); | 
| 433 |  |  |  |  |  |  | } | 
| 434 | 150 | 100 |  |  |  | 2200 |  | 
| 435 |  |  |  |  |  |  | 1; | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 13 |  |  |  |  | 179 | =pod | 
|  | 91 |  |  |  |  | 1955 |  | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | =head1 NAME | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | Workflow::Persister::DBI - Persist workflow and history to DBI database | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | =head1 VERSION | 
| 445 | 12 |  |  | 12 | 1 | 52 |  | 
| 446 |  |  |  |  |  |  | This documentation describes version 1.60 of this package | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 449 | 12 |  |  | 12 | 1 | 54 |  | 
| 450 |  |  |  |  |  |  | <persister name="MainDatabase" | 
| 451 |  |  |  |  |  |  | class="Workflow::Persister::DBI" | 
| 452 |  |  |  |  |  |  | dsn="DBI:mysql:database=workflows" | 
| 453 |  |  |  |  |  |  | user="wf" | 
| 454 |  |  |  |  |  |  | password="mypass"/> | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | <persister name="BackupDatabase" | 
| 457 |  |  |  |  |  |  | class="Workflow::Persister::DBI" | 
| 458 |  |  |  |  |  |  | dsn="DBI:Pg:dbname=workflows" | 
| 459 |  |  |  |  |  |  | user="wf" | 
| 460 |  |  |  |  |  |  | password="mypass" | 
| 461 |  |  |  |  |  |  | date_format="%Y-%m-%d %H:%M" | 
| 462 |  |  |  |  |  |  | autocommit="0" | 
| 463 |  |  |  |  |  |  | workflow_table="wf" | 
| 464 |  |  |  |  |  |  | workflow_sequence="wf_seq" | 
| 465 |  |  |  |  |  |  | history_table="wf_history" | 
| 466 |  |  |  |  |  |  | history_sequence="wf_history_seq"/> | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | <persister name="OtherDatabase" | 
| 469 |  |  |  |  |  |  | class="My::Persister::DBHFromElsewhere" | 
| 470 |  |  |  |  |  |  | driver="mysql" | 
| 471 |  |  |  |  |  |  | /> | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | Main persistence class for storing the workflow and workflow history | 
| 477 |  |  |  |  |  |  | records to a DBI-accessible datasource. | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | =head2 Subclassing: Getting handle from elsewhere | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | A common need to create a subclass is to use a database handle created | 
| 482 |  |  |  |  |  |  | with other means. For instance, OpenInteract has a central | 
| 483 |  |  |  |  |  |  | configuration file for defining datasources, and the datasource will | 
| 484 |  |  |  |  |  |  | be available in a predictable manner. So we can create a subclass to | 
| 485 |  |  |  |  |  |  | provide the database handle on demand from the C<CTX> object available | 
| 486 |  |  |  |  |  |  | from everywhere. A sample implementation is below. (Note that in real | 
| 487 |  |  |  |  |  |  | life we would just use SPOPS for this, but it is still a good | 
| 488 |  |  |  |  |  |  | example.) | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | package Workflow::Persister::DBI::OpenInteractHandle; | 
| 491 |  |  |  |  |  |  |  | 
| 492 |  |  |  |  |  |  | use strict; | 
| 493 |  |  |  |  |  |  | use base qw( Workflow::Persister::DBI ); | 
| 494 |  |  |  |  |  |  | use OpenInteract2::Context qw( CTX ); | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | my @FIELDS = qw( datasource_name ); | 
| 497 |  |  |  |  |  |  | __PACKAGE__->mk_accessors( @FIELDS ); | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | # override parent method, assuming that we set the 'datasource' | 
| 500 |  |  |  |  |  |  | # parameter in the persister declaration | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | sub init { | 
| 503 |  |  |  |  |  |  | my ( $self, $params ) = @_; | 
| 504 |  |  |  |  |  |  | $self->datasource_name( $params->{datasource} ); | 
| 505 |  |  |  |  |  |  | my $ds_config = CTX->lookup_datasource_config( $self->datasource_name ); | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | # delegate the other assignment tasks to the parent class | 
| 508 |  |  |  |  |  |  | $params->{driver} = $ds_config->{driver_name}; | 
| 509 |  |  |  |  |  |  | $self->SUPER::init( $params ); | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | # suppress the parent from trying to connect to the database | 
| 513 |  |  |  |  |  |  | sub create_handle { return undef; } | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | sub handle { | 
| 516 |  |  |  |  |  |  | my ( $self ) = @_; | 
| 517 |  |  |  |  |  |  | return CTX->datasource( $self->datasource_name ); | 
| 518 |  |  |  |  |  |  | } | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | =head2 Subclassing: Changing fieldnames | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | Earlier versions of Workflow used the field 'user' to record in the | 
| 523 |  |  |  |  |  |  | history the user making a state change or comment. Unfortunately | 
| 524 |  |  |  |  |  |  | 'user' is a reserved word in our favorite database, | 
| 525 |  |  |  |  |  |  | PostgreSQL. (Oops.) So in addition to changing the field to an | 
| 526 |  |  |  |  |  |  | assuredly-unreserved word (workflow_user), we made the fieldnames | 
| 527 |  |  |  |  |  |  | customizable by subclasses. | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | Just override either or both of the methods: | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | =head3 get_workflow_fields() | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | Return list of fields in this order: | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | workflow_id, type, state, last_update | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | =head3 get_history_fields() | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | Return list of fields in this order: | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | workflow_hist_id, workflow_id, action, description, | 
| 542 |  |  |  |  |  |  | state, workflow_user, history_date | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | Note that we may cache the results, so don't try and do anything weird | 
| 545 |  |  |  |  |  |  | like change the fieldnames based on the workflow user or something... | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | =head1 METHODS | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | =head2 Public Methods | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | All public methods are inherited from L<Workflow::Persister>. | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | =head2 Private Methods | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | =head3 init( \%params ) | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | Initializes the the instance by setting the connection parameters | 
| 558 |  |  |  |  |  |  | and calling C<create_handle>. You are only required to provide 'dsn', | 
| 559 |  |  |  |  |  |  | which is the full DBI DSN you normally use as the first argument | 
| 560 |  |  |  |  |  |  | to C<connect()>. | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | You can set these parameters in your persister configuration file and | 
| 563 |  |  |  |  |  |  | they will be passed to init. | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | You may also use: | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | =over 4 | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | =item B<user> | 
| 570 |  |  |  |  |  |  |  | 
| 571 |  |  |  |  |  |  | Name of user to login with. | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | =item B<password> | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | Password for C<user> to login with. | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | =item B<date_format> | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | Date format to use when working with the database. Accepts a format string | 
| 580 |  |  |  |  |  |  | that can be processed by the DateTime module. See | 
| 581 |  |  |  |  |  |  | L<http://search.cpan.org/~drolsky/DateTime-0.39/lib/DateTime.pm#strftime_Specifiers> | 
| 582 |  |  |  |  |  |  | for the format options. | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | The default is '%Y-%m-%d %H:%M' for backward compatibility. | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | =item B<autocommit> | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | 0 or 1 to turn autocommit off or on for the database handle. | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | Setting autocommit to off will run Workflow with transactions. If there is | 
| 591 |  |  |  |  |  |  | a failure somewhere and the persister supports it, Workflow will attempt | 
| 592 |  |  |  |  |  |  | to roll back all database activity in the current transaction. | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | If you turn autocommit off, you must still | 
| 595 |  |  |  |  |  |  | commit transactions for L<Workflow::Persister::DBI::ExtraData> yourself. Also, | 
| 596 |  |  |  |  |  |  | if you are sharing the database handle, you must be careful to not pass control | 
| 597 |  |  |  |  |  |  | to the workflow engine with pending transactions as they will be committed if | 
| 598 |  |  |  |  |  |  | the workflow actions are successful. | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | The default autocommit value for the database handle is on. | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | =item B<workflow_table> | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | Table to use for persisting workflow. Default is 'workflow'. | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | =item B<history_table> | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | Table to use for persisting workflow history. Default is | 
| 609 |  |  |  |  |  |  | 'workflow_history'. | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | =back | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | You may also use parameters for the different types of ID | 
| 614 |  |  |  |  |  |  | generators. See below under the C<init_*_generator> for the necessary | 
| 615 |  |  |  |  |  |  | parameters for your database. | 
| 616 |  |  |  |  |  |  |  | 
| 617 |  |  |  |  |  |  | In addition to creating a database handle we parse the C<dsn> to see | 
| 618 |  |  |  |  |  |  | what driver we are using to determine how to generate IDs. We have the | 
| 619 |  |  |  |  |  |  | ability to use automatically generated IDs for PostgreSQL, MySQL, and | 
| 620 |  |  |  |  |  |  | SQLite. If your database is not included a randomly generated ID will | 
| 621 |  |  |  |  |  |  | be used. (Default length of 8 characters, which you can modify with a | 
| 622 |  |  |  |  |  |  | C<id_length> parameter.) | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | You can also create your own adapter for a different type of | 
| 625 |  |  |  |  |  |  | database. Just check out the existing | 
| 626 |  |  |  |  |  |  | L<Workflow::Persister::DBI::AutoGeneratedId> and | 
| 627 |  |  |  |  |  |  | L<Workflow::Persister::DBI::SequenceId> classes for examples. | 
| 628 |  |  |  |  |  |  |  | 
| 629 |  |  |  |  |  |  | =head3 assign_generators( $driver, \%params ) | 
| 630 |  |  |  |  |  |  |  | 
| 631 |  |  |  |  |  |  | Given C<$driver> and the persister parameters in C<\%params>, assign | 
| 632 |  |  |  |  |  |  | the appropriate ID generators for both the workflow and history | 
| 633 |  |  |  |  |  |  | tables. | 
| 634 |  |  |  |  |  |  |  | 
| 635 |  |  |  |  |  |  | Returns: nothing, but assigns the object properties | 
| 636 |  |  |  |  |  |  | C<workflow_id_generator> and C<history_id_generator>. | 
| 637 |  |  |  |  |  |  |  | 
| 638 |  |  |  |  |  |  | =head3 assign_tables( \%params ) | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | Assign the table names from C<\%params> (using 'workflow_table' and | 
| 641 |  |  |  |  |  |  | 'history_table') or use the defaults 'workflow' and 'workflow_history'. | 
| 642 |  |  |  |  |  |  |  | 
| 643 |  |  |  |  |  |  | Returns: nothing, but assigns the object properties C<workflow_table> | 
| 644 |  |  |  |  |  |  | and C<history_table>. | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | =head3 init_postgres_generators( \%params ) | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | Create ID generators for the workflow and history tables using | 
| 649 |  |  |  |  |  |  | PostgreSQL sequences. You can specify the sequences used for the | 
| 650 |  |  |  |  |  |  | workflow and history tables: | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | =over 4 | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | =item B<workflow_sequence> | 
| 655 |  |  |  |  |  |  |  | 
| 656 |  |  |  |  |  |  | Sequence for the workflow table. Default: 'workflow_seq' | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | =item B<history_sequence> | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | Sequence for the workflow history table. Default: | 
| 661 |  |  |  |  |  |  | 'workflow_history_seq' | 
| 662 |  |  |  |  |  |  |  | 
| 663 |  |  |  |  |  |  | =back | 
| 664 |  |  |  |  |  |  |  | 
| 665 |  |  |  |  |  |  | =head3 init_mysql_generators( \%params ) | 
| 666 |  |  |  |  |  |  |  | 
| 667 |  |  |  |  |  |  | Create ID generators for the workflow and history tables using | 
| 668 |  |  |  |  |  |  | the MySQL 'auto_increment' type. No parameters are necessary. | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | =head3 init_sqlite_generators( \%params ) | 
| 671 |  |  |  |  |  |  |  | 
| 672 |  |  |  |  |  |  | Create ID generators for the workflow and history tables using | 
| 673 |  |  |  |  |  |  | the SQLite implicit increment. No parameters are necessary. | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | =head3 init_random_generators( \%params ) | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | Create ID generators for the workflow and history tables using | 
| 678 |  |  |  |  |  |  | a random set of characters. You can specify: | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | =over 4 | 
| 681 |  |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | =item B<id_length> | 
| 683 |  |  |  |  |  |  |  | 
| 684 |  |  |  |  |  |  | Length of character sequence to generate. Default: 8. | 
| 685 |  |  |  |  |  |  |  | 
| 686 |  |  |  |  |  |  | =back | 
| 687 |  |  |  |  |  |  |  | 
| 688 |  |  |  |  |  |  | =head3 init_oracle_generators | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | Create ID generators for the workflow and history tables using | 
| 691 |  |  |  |  |  |  | the Oracle sequences. No parameters are necessary. | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | =head3 create_handle | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | Creates a database connection using DBI's C<connect> method and returns | 
| 696 |  |  |  |  |  |  | the resulting database handle. Override this method if you want to set | 
| 697 |  |  |  |  |  |  | different options than the hard-coded ones, or when you want to use a | 
| 698 |  |  |  |  |  |  | handle from elsewhere. | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  | The default implementation hard-codes these database handle settings: | 
| 701 |  |  |  |  |  |  |  | 
| 702 |  |  |  |  |  |  | $dbh->{RaiseError} = 1; | 
| 703 |  |  |  |  |  |  | $dbh->{PrintError} = 0; | 
| 704 |  |  |  |  |  |  | $dbh->{ChopBlanks} = 1; | 
| 705 |  |  |  |  |  |  |  | 
| 706 |  |  |  |  |  |  | =head3 create_workflow | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | Serializes a workflow into the persistance entity configured by our workflow. | 
| 709 |  |  |  |  |  |  |  | 
| 710 |  |  |  |  |  |  | Takes a single parameter: a workflow object | 
| 711 |  |  |  |  |  |  |  | 
| 712 |  |  |  |  |  |  | Returns a single value, a id for unique identification of out serialized | 
| 713 |  |  |  |  |  |  | workflow for possible deserialization. | 
| 714 |  |  |  |  |  |  |  | 
| 715 |  |  |  |  |  |  | =head3 fetch_workflow | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | Deserializes a workflow from the persistance entity configured by our workflow. | 
| 718 |  |  |  |  |  |  |  | 
| 719 |  |  |  |  |  |  | Takes a single parameter: the unique id assigned to our workflow upon | 
| 720 |  |  |  |  |  |  | serialization (see L</create_workflow>). | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | Returns a hashref consisting of two keys: | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | =over | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | =item * state, the workflows current state | 
| 727 |  |  |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | =item * last_update, date indicating last update | 
| 729 |  |  |  |  |  |  |  | 
| 730 |  |  |  |  |  |  | =back | 
| 731 |  |  |  |  |  |  |  | 
| 732 |  |  |  |  |  |  | =head3 update_workflow | 
| 733 |  |  |  |  |  |  |  | 
| 734 |  |  |  |  |  |  | Updates a serialized workflow in the persistance entity configured by our | 
| 735 |  |  |  |  |  |  | workflow. | 
| 736 |  |  |  |  |  |  |  | 
| 737 |  |  |  |  |  |  | Takes a single parameter: a workflow object | 
| 738 |  |  |  |  |  |  |  | 
| 739 |  |  |  |  |  |  | Returns: Nothing | 
| 740 |  |  |  |  |  |  |  | 
| 741 |  |  |  |  |  |  | =head3 create_history | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | Serializes history records associated with a workflow object | 
| 744 |  |  |  |  |  |  |  | 
| 745 |  |  |  |  |  |  | Takes two parameters: a workflow object and an array of workflow history objects | 
| 746 |  |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | Returns: provided array of workflow history objects upon success | 
| 748 |  |  |  |  |  |  |  | 
| 749 |  |  |  |  |  |  | =head3 fetch_history | 
| 750 |  |  |  |  |  |  |  | 
| 751 |  |  |  |  |  |  | Deserializes history records associated with a workflow object | 
| 752 |  |  |  |  |  |  |  | 
| 753 |  |  |  |  |  |  | Takes a single parameter: a workflow object | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | Returns an array of workflow history objects upon success | 
| 756 |  |  |  |  |  |  |  | 
| 757 |  |  |  |  |  |  | =head3 commit_transaction ( $wf ) | 
| 758 |  |  |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | Commit the transaction for a workflow if autocommit is not enabled. | 
| 760 |  |  |  |  |  |  |  | 
| 761 |  |  |  |  |  |  | Returns nothing | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | =head3 rollback_transaction | 
| 764 |  |  |  |  |  |  |  | 
| 765 |  |  |  |  |  |  | Rollsback the transaction for a workflow if autocommit is not enabled. | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | Returns nothing | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 771 |  |  |  |  |  |  |  | 
| 772 |  |  |  |  |  |  | =over | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | =item L<Workflow> | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | =item L<Workflow::Persister> | 
| 777 |  |  |  |  |  |  |  | 
| 778 |  |  |  |  |  |  | =item L<Workflow::History> | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | =item L<DBI> | 
| 781 |  |  |  |  |  |  |  | 
| 782 |  |  |  |  |  |  | =back | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | Copyright (c) 2003-2022 Chris Winters. All rights reserved. | 
| 787 |  |  |  |  |  |  |  | 
| 788 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 789 |  |  |  |  |  |  | it under the same terms as Perl itself. | 
| 790 |  |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | Please see the F<LICENSE> | 
| 792 |  |  |  |  |  |  |  | 
| 793 |  |  |  |  |  |  | =head1 AUTHORS | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | Please see L<Workflow> | 
| 796 |  |  |  |  |  |  |  | 
| 797 |  |  |  |  |  |  | =cut |