File Coverage

blib/lib/Data/Record/Serialize/Encode/dbi.pm
Criterion Covered Total %
statement 120 168 71.4
branch 36 72 50.0
condition 8 17 47.0
subroutine 25 31 80.6
pod 5 6 83.3
total 194 294 65.9


line stmt bran cond sub pod time code
1             package Data::Record::Serialize::Encode::dbi;
2              
3             # ABSTRACT: store a record in a database
4              
5 1     1   406157 use v5.12;
  1         4  
6 1     1   8 use Moo::Role;
  1         2  
  1         9  
7              
8             use Data::Record::Serialize::Error {
9 1         21 errors => [
10             qw( param
11             connect
12             schema
13             create
14             insert
15             sqlite_backend
16             ),
17             ],
18             },
19 1     1   545 -all;
  1         6  
20              
21             our $VERSION = '1.05';
22              
23 1     1   2544 use Data::Record::Serialize::Types -types;
  1         4  
  1         10  
24              
25 1     1   1257 use SQL::Translator;
  1         260717  
  1         45  
26 1     1   13 use SQL::Translator::Schema;
  1         2  
  1         28  
27 1     1   5 use Types::Standard -types;
  1         2  
  1         12  
28 1     1   9747 use Types::Common::String qw( NonEmptySimpleStr );
  1         39523  
  1         10  
29              
30 1     1   1119 use List::Util 1.33 qw[ pairmap any ];
  1         24  
  1         78  
31              
32 1     1   8 use DBI;
  1         2  
  1         50  
33              
34 1     1   8 use namespace::clean;
  1         3  
  1         28  
35              
36              
37              
38              
39              
40              
41              
42             has dsn => (
43             is => 'ro',
44             required => 1,
45             coerce => sub {
46             my $arg = 'ARRAY' eq ref $_[0] ? $_[0] : [ $_[0] ];
47             my @dsn;
48             my @args;
49             for my $el ( @{$arg} ) {
50              
51             my $ref = ref $el;
52             unless ( $ref eq 'ARRAY' || $ref eq 'HASH' ) {
53             push( @dsn, $el );
54             next;
55             }
56              
57             my @arr = $ref eq 'ARRAY' ? @{$el} : %{$el};
58              
59             push @args, pairmap { join( q{=}, $a, $b ) } @arr;
60             }
61             my $args = join( q{;}, @args );
62             push @dsn, $args if length $args;
63             unshift @dsn, 'dbi' unless $dsn[0] =~ /^dbi/;
64             return join( q{:}, @dsn );
65             },
66             );
67              
68             has _cached => (
69             is => 'ro',
70             default => 0,
71             init_arg => 'cached',
72             );
73              
74              
75              
76              
77              
78              
79              
80              
81             has table => (
82             is => 'ro',
83             isa => Str,
84             required => 1,
85             );
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97             has schema => (
98             is => 'ro',
99             isa => Maybe [NonEmptySimpleStr],
100             );
101              
102              
103              
104              
105              
106              
107              
108             has drop_table => (
109             is => 'ro',
110             isa => Bool,
111             default => 0,
112             );
113              
114              
115              
116              
117              
118              
119              
120             has create_table => (
121             is => 'ro',
122             isa => Bool,
123             default => 1,
124             );
125              
126              
127              
128              
129              
130              
131              
132             has primary => (
133             is => 'ro',
134             isa => ArrayOfStr,
135             coerce => 1,
136             default => sub { [] },
137             );
138              
139              
140              
141              
142              
143              
144              
145             has db_user => (
146             is => 'ro',
147             isa => Str,
148             default => q{},
149             );
150              
151              
152              
153              
154              
155              
156              
157             has db_pass => (
158             is => 'ro',
159             isa => Str,
160             default => q{},
161             );
162              
163             has _sth => (
164             is => 'rwp',
165             init_arg => undef,
166             );
167              
168             has _dbh => (
169             is => 'rwp',
170             init_arg => undef,
171             clearer => 1,
172             predicate => 1,
173             );
174              
175              
176              
177              
178              
179              
180              
181             has column_defs => (
182             is => 'rwp',
183             lazy => 1,
184             clearer => 1,
185             init_arg => undef,
186             builder => sub {
187 0     0   0 my $self = shift;
188              
189 0         0 my @column_defs;
190 0         0 for my $field ( @{ $self->output_fields } ) {
  0         0  
191             push @column_defs,
192             join( q{ },
193             $field,
194 0         0 $self->output_types->{$field},
195             ( 'primary key' )x!!( $self->primary eq $field ) );
196             }
197              
198 0         0 return join ', ', @column_defs;
199             },
200             );
201              
202              
203              
204              
205              
206              
207              
208             has batch => (
209             is => 'ro',
210             isa => Int,
211             default => 100,
212             coerce => sub { $_[0] > 1 ? $_[0] : 0 },
213             );
214              
215              
216              
217              
218              
219              
220              
221             has dbitrace => ( is => 'ro', );
222              
223              
224              
225              
226              
227              
228              
229              
230              
231              
232              
233              
234              
235             has queue => (
236             is => 'ro',
237             init_arg => undef,
238             default => sub { [] },
239             );
240              
241             around '_build__nullified' => sub {
242             my $orig = shift;
243             my $self = $_[0];
244              
245             my $nullified = $self->$orig( @_ );
246              
247             # defer to the caller
248             return $nullified if $self->has_nullify;
249              
250             # add all of the numeric fields
251             [ @{ $self->numeric_fields } ];
252              
253             };
254              
255             my %MapTypes = (
256             Pg => { S => 'text', N => 'real', I => 'integer', B => 'boolean' },
257             SQLite => { S => 'text', N => 'real', I => 'integer', B => 'integer' },
258             Default => { S => 'text', N => 'real', I => 'integer', B => 'integer' },
259             );
260              
261             sub _map_types {
262 15   33 15   5506 $MapTypes{ $_[0]->_dbi_driver } // $MapTypes{Default};
263             }
264              
265              
266              
267              
268              
269              
270              
271              
272              
273 0 0   0 1 0 sub to_bool { $_[0] ? 1 : 0 }
274              
275             sub _table_exists {
276 5     5   16 my $self = shift;
277              
278             # DBD::Sybase doesn't filter, so need to search
279 5         94 my $matches
280             = $self->_dbh->table_info( q{%}, $self->schema, $self->table, 'TABLE' )->fetchall_arrayref;
281 5     1   5118 return any { $_->[2] eq $self->table } @{$matches};
  1         19  
  5         56  
282             }
283              
284             sub _fq_table_name {
285 5     5   16 my $self = shift;
286 5 50       41 defined $self->schema ? $self->schema . q{.} . $self->table : $self->table;
287             }
288              
289             has _dsn_components => (
290             is => 'lazy',
291             init_arg => undef,
292             builder => sub {
293 5 50   5   121 my @dsn = DBI->parse_dsn( $_[0]->dsn )
294             or error( 'param', 'unable to parse DSN: ', $_[0]->dsn );
295 5         171 \@dsn;
296             },
297             );
298              
299             sub _dbi_driver {
300 25     25   443 $_[0]->_dsn_components->[1];
301             }
302              
303             my %producer = (
304             DB2 => 'DB2',
305             MySQL => 'mysql',
306             Oracle => 'Oracle',
307             Pg => 'PostgreSQL',
308             SQLServer => 'SQLServer',
309             SQLite => 'SQLite',
310             Sybase => 'Sybase',
311             );
312              
313             has _producer => (
314             is => 'lazy',
315             init_arg => undef,
316             builder => sub {
317 5     5   95 my $dbi_driver = $_[0]->_dbi_driver;
318 5 50       234 $producer{$dbi_driver} || $dbi_driver;
319             },
320             );
321              
322              
323              
324              
325              
326              
327              
328             sub setup {
329 5     5 0 262 my $self = shift;
330              
331 5 50       35 return if $self->_has_dbh;
332              
333 5         42 my %attr = (
334             AutoCommit => !$self->batch,
335             RaiseError => 1,
336             PrintError => 0,
337             ( 'private_' . __PACKAGE__ ) => __FILE__ . __LINE__,
338             );
339              
340              
341 5         14 my $dbd = $self->_dbi_driver;
342              
343 5 50       93 if ( $dbd eq 'Sybase' ) {
344 0         0 $attr{syb_quoted_identifier} = 1;
345             }
346              
347 5 50       26 my $connect = $self->_cached ? 'connect_cached' : 'connect';
348              
349 5 50       49 $self->_set__dbh( DBI->$connect( $self->dsn, $self->db_user, $self->db_pass, \%attr ) )
350             or error( 'connect', 'error connecting to ', $self->dsn, "\n" );
351              
352 5 50       12182 $self->_dbh->trace( $self->dbitrace )
353             if $self->dbitrace;
354              
355 5 50 33     49 if ( $self->drop_table || ( $self->create_table && !( my $table_exists = $self->_table_exists ) ) )
      66        
356             {
357             # in case the first half of the conditional in the above if
358             # statement is true, the call to $self->_table_exists isn't
359             # made, so $table_exists is not defined.
360 5   66     21 $table_exists //= $self->_table_exists;
361              
362             ## no critic (Variables::ProhibitUnusedVarsStricter)
363 5 50       24 my @guards = $dbd eq 'Sybase' ? _patch_sqlt_producer_sybase() : ();
364              
365             my $tr = SQL::Translator->new(
366             from => sub {
367 5     5   2885 my $schema = $_[0]->schema;
368 5 50       4887 my $table = $schema->add_table( name => $self->_fq_table_name )
369             or error( 'schema', $schema->error );
370              
371 5         5157 for my $field_name ( @{ $self->output_fields } ) {
  5         97  
372             $table->add_field(
373             name => $field_name,
374 15 50       13410 data_type => $self->output_types->{$field_name} ) or error( 'schema', $table->error );
375             }
376              
377 5 50       7464 if ( @{ $self->primary } ) {
  5         41  
378 0 0       0 $table->primary_key( @{ $self->primary } )
  0         0  
379             or error( 'schema', $table->error );
380             }
381              
382 5         25 1;
383             },
384 5   66     185 to => $self->_producer,
385             producer_args => { no_transaction => 1 },
386             add_drop_table => $self->drop_table && $table_exists,
387             no_comments => 1,
388             );
389              
390 5         29317 my @sql = $tr->translate;
391 5 50       25947 defined $sql[0] or error( 'schema', $tr->error );
392              
393 5         37 _do_sql( $self->_dbh, 'create', @sql );
394 5 100       65310 $self->_dbh->commit if $self->batch;
395             }
396              
397             my $sql = sprintf(
398             'insert into %s (%s) values (%s)',
399             $self->_dbh->quote_identifier( undef, $self->schema, $self->table ),
400 5         4126 join( q{,}, @{ $self->output_fields } ),
401 5         158 join( q{,}, ( q{?} ) x @{ $self->output_fields } ),
  5         253  
402             );
403              
404 5         166 $self->_set__sth( $self->_dbh->prepare( $sql ) );
405              
406 5         612 return;
407             }
408              
409             # SQL::Translator::Producer::Sybase has behaviors and bugs.
410             sub _patch_sqlt_producer_sybase {
411              
412 0     0   0 require SQL::Translator::Producer::Sybase;
413 0         0 require Monkey::Patch;
414 0         0 my %scope;
415              
416             my @guards;
417              
418             # Behavior: It tries to be 'helpful' and renames a table if it
419             # collides with a table it has already seen. There's no way to
420             # turn that off. In this case, we may dropping/creating a table
421             # multiple times, and having the table name change is not useful.
422             # We turn its concept of "global name space" into a local one for
423             # tables/indices/constraints. args[2] is the namespace hash, and
424             # mk_name is currently passed a non-empty entry *only* for fields,
425             # so we make sure to only give it a hash when its not specified
426             # (and thus not a field), as we don't want to mix field names and
427             # the table/indices/constraints level names.
428              
429             push @guards, Monkey::Patch::patch_package(
430             'SQL::Translator::Producer::Sybase',
431             'mk_name',
432             sub {
433 0     0   0 my ( $orig, @args ) = @_;
434 0   0     0 $args[2] ||= \%scope;
435 0         0 $orig->( @args );
436 0         0 } );
437              
438             # Bug: add_drop_table attribute doesn't add the drop table
439             # command. future proof by chcking if the Sybase producer
440             # implements it correctly and only monkey-patching it if it
441             # doesn't.
442 0         0 my $need_to_add_drop_table = do {
443             my $tr = SQL::Translator->new(
444             from => sub {
445 0     0   0 require SQL::Translator::Schema::Table;
446 0         0 my $table = SQL::Translator::Schema::Table->new( name => 'foo' );
447 0         0 $table->add_field( name => 'foo_id', date_type => 'integer' );
448 0         0 $_[0]->schema->add_table( $table );
449 0         0 1;
450             },
451 0         0 to => 'Sybase',
452             add_drop_table => 1,
453             no_comments => 1,
454             );
455 0         0 $tr->translate !~ /DROP TABLE/;
456             };
457              
458 0 0       0 if ( $need_to_add_drop_table ) {
459             push @guards, Monkey::Patch::patch_package(
460             'SQL::Translator::Producer::Sybase',
461             'produce',
462             sub {
463 0     0   0 my ( $orig, @args ) = @_;
464              
465 0         0 my $translator = $args[0];
466              
467 0 0       0 return $orig->( @args ) unless $translator->add_drop_table;
468              
469 0 0       0 if ( wantarray ) { ## no critic (Community::Wantarray)
470 0         0 my @orig = $orig->( @args );
471 0 0       0 return map { /CREATE TABLE (\S+) [(]/ ? ( qq[DROP TABLE $1\n\n], $_ ) : $_ } @orig;
  0         0  
472             }
473             else {
474 0         0 my $output = $orig->( @args );
475 0         0 $output =~ s/CREATE TABLE\s+(\S+)\s+[(]/DROP TABLE $1\n$&/g;
476 0         0 return $output;
477             }
478              
479 0         0 } );
480              
481             }
482              
483 0         0 return @guards;
484             }
485              
486             sub _do_sql {
487 5     5   16 my ( $dbh, $subsys, @sql ) = @_;
488              
489 5         86 my $in_txn = !$dbh->{AutoCommit};
490              
491 5         18 my $statement;
492             eval {
493 5 100       47 $dbh->begin_work unless $in_txn;
494 5         37 while ( $statement = shift @sql ) {
495 6 50       510 die if !defined $dbh->do( $statement );
496             }
497 5 100       20916 $dbh->commit unless $in_txn;
498 5         50 1;
499 5 50       11 } or do {
500 0         0 my $e = $@;
501 0 0       0 $dbh->rollback unless $in_txn;
502 0         0 $statement =~ s/\n/ /g;
503 0         0 error( $subsys, { msg => $e, payload => $statement } );
504             };
505              
506             }
507              
508              
509              
510              
511              
512              
513              
514              
515              
516              
517              
518              
519              
520              
521              
522              
523              
524              
525              
526              
527              
528              
529              
530              
531              
532              
533              
534              
535              
536              
537              
538              
539              
540              
541              
542              
543              
544              
545              
546              
547              
548              
549              
550              
551              
552              
553              
554              
555              
556              
557              
558              
559             sub flush {
560 7     7 1 44 my $self = shift;
561              
562 7 50       37 return 1 unless $self->_has_dbh;
563              
564 7         25 my $queue = $self->queue;
565              
566 7 100       39 if ( @{$queue} ) {
  7         33  
567 6         32 my $last;
568              
569 6         17 my $ret = eval { $self->_sth->execute( @$last ) while $last = shift @{$queue}; 1; };
  6         14  
  30         2468  
  6         86  
570 6         25 my $error = $@;
571              
572 6         93479 $self->_dbh->commit;
573              
574 6 50       135 if ( !defined $ret ) {
575 0         0 unshift @{$queue}, $last;
  0         0  
576              
577 0         0 my %query;
578 0         0 @query{ @{ $self->output_fields } } = @$last;
  0         0  
579 0         0 error( 'insert', { msg => "Transaction aborted: $error", payload => \%query } );
580             }
581             }
582              
583 7         78 1;
584             }
585              
586              
587              
588              
589              
590              
591              
592              
593              
594              
595              
596              
597              
598              
599              
600              
601              
602              
603             sub send {
604 30     30 1 17259 my $self = shift;
605              
606 30 100       155 if ( $self->batch ) {
607 24         71 push @{ $self->queue }, [ @{ $_[0] }{ @{ $self->output_fields } } ];
  24         81  
  24         395  
  24         580  
608             $self->flush
609 24 100       70 if @{ $self->queue } == $self->batch;
  24         248  
610              
611             }
612             else {
613 6 50       23 eval { $self->_sth->execute( @{ $_[0] }{ @{ $self->output_fields } } ); 1; }
  6         24  
  6         94474  
  6         172  
  6         245  
614             or error( 'insert', { msg => "record insertion failed: $@", payload => $_[0] } );
615             }
616             }
617              
618              
619             after '_trigger_output_fields' => sub {
620             $_[0]->clear_column_defs;
621             };
622              
623             after '_trigger_output_types' => sub {
624             $_[0]->clear_column_defs;
625             };
626              
627              
628              
629              
630              
631              
632              
633              
634              
635              
636              
637              
638              
639              
640              
641              
642              
643              
644              
645              
646              
647             sub close {
648 5     5 1 825 my $self = shift;
649              
650 5 50       41 return 1 unless $self->_has_dbh;
651              
652 5 100       46 $self->flush if $self->batch;
653 5         1020 $self->_dbh->disconnect;
654 5         456 $self->_clear_dbh;
655              
656 5         151 1;
657             }
658              
659              
660              
661              
662              
663              
664              
665              
666              
667              
668              
669              
670             sub DEMOLISH {
671 5     5 1 83244 my $self = shift;
672              
673             warnings::warnif( 'Data::Record::Serialize::Encode::dbi::queue',
674             __PACKAGE__ . ': record queue is not empty in object destruction' )
675 5 50       17 if @{ $self->queue };
  5         35  
676              
677 5 50       96 $self->_dbh->disconnect
678             if $self->_has_dbh;
679              
680             }
681              
682              
683             # these are required by the Sink/Encode interfaces but should never be
684             # called in the ordinary run of things.
685              
686              
687              
688              
689              
690              
691              
692              
693             with 'Data::Record::Serialize::Role::EncodeAndSink';
694              
695             1;
696              
697             #
698             # This file is part of Data-Record-Serialize-Encode-dbi
699             #
700             # This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
701             #
702             # This is free software, licensed under:
703             #
704             # The GNU General Public License, Version 3, June 2007
705             #
706              
707             __END__
708              
709             =pod
710              
711             =for :stopwords Diab Jerius Smithsonian Astrophysical Observatory DDL DSN Postgres Sybase
712             msg truthy
713              
714             =head1 NAME
715              
716             Data::Record::Serialize::Encode::dbi - store a record in a database
717              
718             =head1 VERSION
719              
720             version 1.05
721              
722             =head1 SYNOPSIS
723              
724             use Data::Record::Serialize;
725              
726             my $s = Data::Record::Serialize->new( encode => 'sqlite', ... );
727              
728             $s->send( \%record );
729              
730             =head1 DESCRIPTION
731              
732             B<Data::Record::Serialize::Encode::dbi> writes a record to a database using
733             L<DBI>.
734              
735             It performs both the L<Data::Record::Serialize::Role::Encode> and
736             L<Data::Record::Serialize::Role::Sink> roles.
737              
738             B<You cannot construct this directly>. You must use
739             L<Data::Record::Serialize/new>.
740              
741             =head2 Types
742              
743             Field types are recognized and converted to SQL types via the following map:
744              
745             S => 'text'
746             N => 'real'
747             I => 'integer'
748              
749             For Postgres, C<< B => 'boolean' >>. For other databases, C<< B => 'integer' >>.
750             This encoder handles transformation of the input "truthy" Boolean value into
751             a form appropriate for the database to ingest.
752              
753             =head2 NULL values
754              
755             By default numeric fields are set to C<NULL> if they are empty. This
756             can be changed by setting the C<nullify> attribute.
757              
758             =head2 Performance
759              
760             Records are by default written to the database in batches (see the
761             C<batch> attribute) to improve performance. Each batch is performed
762             as a single transaction. If there is an error during the transaction,
763             record insertions during the transaction are I<not> rolled back.
764              
765             =head2 Errors
766              
767             Transaction errors result in an exception in the
768             C<Data::Record::Serialize::Error::Encode::dbi::insert> class. See
769             L<Data::Record::Serialize::Error> for more information on exception
770             objects.
771              
772             =head2 Compatibility
773              
774             This module has been tested on SQLite, PostgreSQL, and Sybase. See
775             F<t/encoders/dbi.t> for more information on how to test against
776             non-SQLite databases.
777              
778             L<SQL::Translator> is used to generate the DDL; unfortunately its
779             Sybase DDL producer has some issues/bugs and is temporarily
780             monkey-patched to work around them.
781              
782             =head1 OBJECT ATTRIBUTES
783              
784             =head2 C<dsn>
785              
786             The value passed to the constructor.
787              
788             =head2 C<table>
789              
790             The value passed to the constructor.
791              
792             =head2 C<schema>
793              
794             The value passed to the constructor.
795              
796             =head2 C<drop_table>
797              
798             The value passed to the constructor.
799              
800             =head2 C<create_table>
801              
802             The value passed to the constructor.
803              
804             =head2 C<primary>
805              
806             The value passed to the constructor.
807              
808             =head2 C<db_user>
809              
810             The value passed to the constructor.
811              
812             =head2 C<db_pass>
813              
814             The value passed to the constructor.
815              
816             =head2 C<batch>
817              
818             The value passed to the constructor.
819              
820             =head2 C<dbitrace>
821              
822             The value passed to the constructor.
823              
824             =head1 METHODS
825              
826             =head2 C<queue>
827              
828             $queue = $obj->queue;
829              
830             The queue containing records not yet successfully transmitted
831             to the database. This is only of interest if L</batch> is not C<0>.
832              
833             Each element is an array containing values to be inserted into the database,
834             in the same order as the fields in L<Data::Serialize/output_fields>.
835              
836             =head2 to_bool
837              
838             $bool = $self->to_bool( $truthy );
839              
840             Convert a truthy value to something that the JSON encoders will recognize as a boolean.
841              
842             =head2 flush
843              
844             $s->flush;
845              
846             Flush the queue of records to the database. It returns true if
847             all of the records have been successfully written.
848              
849             If writing fails:
850              
851             =over
852              
853             =item *
854              
855             Writing of records ceases.
856              
857             =item *
858              
859             The failing record is left at the head of the queue. This ensures
860             that it is possible to retry writing the record.
861              
862             =item *
863              
864             an exception object (in the
865             C<Data::Record::Serialize::Error::Encode::dbi::insert> class) will be
866             thrown. The failing record (in its final form after formatting, etc)
867             is available via the object's C<payload> method.
868              
869             =back
870              
871             If a record fails to be written, it will still be queued for the next
872             attempt at writing to the database. If this behavior is undesired,
873             make sure to remove it from the queue:
874              
875             use Data::Dumper;
876              
877             if ( ! eval { $output->flush } ) {
878             warn "$@", Dumper( $@->payload );
879             shift $output->queue->@*;
880             }
881              
882             As an example of completely flushing the queue while notifying of errors:
883              
884             use Data::Dumper;
885              
886             until ( eval { $output->flush } ) {
887             warn "$@", Dumper( $@->payload );
888             shift $output->queue->@*;
889             }
890              
891             =head2 send
892              
893             $s->send( \%record );
894              
895             Send a record to the database.
896             If there is an error, an exception object (with class
897             C<Data::Record::Serialize::Error::Encode::dbi::insert>) will be
898             thrown, and the record which failed to be written will be available
899             via the object's C<payload> method.
900              
901             If in L</batch> mode, the record is queued for later transmission.
902             When the number of records queued reaches that specified by the
903             L</batch> attribute, the C<flush> method is called. See L</flush> for
904             more information on how errors are handled.
905              
906             =head2 close
907              
908             $s->close;
909              
910             Close the database handle. If writing is batched, records in the queue
911             are written to the database via L</flush>. An exception will be thrown
912             if a record cannot be written. See L</flush> for more details.
913              
914             As an example of draining the queue while notifying of errors:
915              
916             use Data::Dumper;
917              
918             until ( eval { $output->close } ) {
919             warn "$@", Dumper( $@->payload );
920             shift $output->queue->@*;
921             }
922              
923             =head2 DEMOLISH
924              
925             This method is called when the object is destroyed. It closes the
926             database handle B<but does not flush the record queue>.
927              
928             A warning is emitted if the record queue is not empty; turn off the
929             C<Data::Record::Serialize::Encode::dbi::queue> warning to silence it.
930              
931             =head1 INTERNALS
932              
933             =for Pod::Coverage has_schema
934              
935             =for Pod::Coverage _has_dbh
936             _clear_dbh
937              
938             =for Pod::Coverage setup
939              
940             =for Pod::Coverage say
941             print
942             encode
943              
944             =head1 CONSTRUCTOR OPTIONS
945              
946             =over
947              
948             =item C<dsn>
949              
950             I<Required> The DBI Data Source Name (DSN) passed to B<L<DBI>>. It
951             may either be a string or an arrayref containing strings or arrayrefs,
952             which should contain key-value pairs. Elements in the sub-arrays are
953             joined with C<=>, elements in the top array are joined with C<:>. For
954             example,
955              
956             [ 'SQLite', { dbname => $db } ]
957              
958             is transformed to
959              
960             SQLite:dbname=$db
961              
962             The standard prefix of C<dbi:> will be added if not present.
963              
964             =item C<cached>
965              
966             If true, the database connection is made with L<DBI::connect_cached|DBI/connect_cached> rather than
967             L<DBI::connect|DBI/connect>
968              
969             =item C<table>
970              
971             I<Required> The name of the table in the database which will contain the records.
972             It will be created if it does not exist.
973              
974             =item C<schema>
975              
976             The schema to which the table belongs. Optional.
977              
978             =item C<drop_table>
979              
980             If true, the table is dropped and a new one is created.
981              
982             =item C<create_table>
983              
984             If true, a table will be created if it does not exist.
985              
986             =item C<primary>
987              
988             A single output column name or an array of output column names which
989             should be the primary key(s). If not specified, no primary keys are
990             defined.
991              
992             =item C<db_user>
993              
994             The name of the database user
995              
996             =item C<db_pass>
997              
998             The database password
999              
1000             =item C<batch>
1001              
1002             The number of rows to write to the database at once. This defaults to 100.
1003              
1004             If greater than 1, C<batch> rows are cached and then sent out in a
1005             single transaction. See L</Performance> for more information.
1006              
1007             =item C<dbitrace>
1008              
1009             A trace setting passed to L<DBI>.
1010              
1011             =back
1012              
1013             =head1 ATTRIBUTES
1014              
1015             These attributes are available in addition to the standard attributes
1016             defined for L<< Data::Record::Serialize::new|Data::Record::Serialize/new >>.
1017              
1018             =head1 SUPPORT
1019              
1020             =head2 Bugs
1021              
1022             Please report any bugs or feature requests to bug-data-record-serialize-encode-dbi@rt.cpan.org or through the web interface at: L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Record-Serialize-Encode-dbi>
1023              
1024             =head2 Source
1025              
1026             Source is available at
1027              
1028             https://gitlab.com/djerius/data-record-serialize-encode-dbi
1029              
1030             and may be cloned from
1031              
1032             https://gitlab.com/djerius/data-record-serialize-encode-dbi.git
1033              
1034             =head1 AUTHOR
1035              
1036             Diab Jerius <djerius@cpan.org>
1037              
1038             =head1 COPYRIGHT AND LICENSE
1039              
1040             This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
1041              
1042             This is free software, licensed under:
1043              
1044             The GNU General Public License, Version 3, June 2007
1045              
1046             =cut