File Coverage

lib/Workflow/Persister/DBI.pm
Criterion Covered Total %
statement 230 261 88.1
branch 47 68 69.1
condition 6 17 35.2
subroutine 30 33 90.9
pod 17 17 100.0
total 330 396 83.3


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