File Coverage

blib/lib/ArrayDataRole/Source/DBI.pm
Criterion Covered Total %
statement 73 80 91.2
branch 28 42 66.6
condition 5 11 45.4
subroutine 10 11 90.9
pod 1 8 12.5
total 117 152 76.9


line stmt bran cond sub pod time code
1             package ArrayDataRole::Source::DBI;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2021-05-10'; # DATE
5             our $DIST = 'ArrayDataRoles-Standard'; # DIST
6             our $VERSION = '0.006'; # VERSION
7              
8 1     1   551 use 5.010001;
  1         5  
9 1     1   5 use Role::Tiny;
  1         3  
  1         6  
10 1     1   167 use Role::Tiny::With;
  1         3  
  1         880  
11             with 'ArrayDataRole::Spec::Basic';
12              
13             sub new {
14 1     1 1 65107 my ($class, %args) = @_;
15              
16 1         5 my $dsn = delete $args{dsn};
17 1         4 my $user = delete $args{user};
18 1         3 my $password = delete $args{password};
19 1         3 my $dbh = delete $args{dbh};
20 1 50       6 if (defined $dbh) {
    0          
21             } elsif (defined $dsn) {
22 0         0 require DBI;
23 0         0 $dbh = DBI->connect($dsn, $user, $password, {RaiseError=>1});
24             }
25              
26 1         23 my $sth = delete $args{sth};
27 1         21 my $sth_bind_params = delete $args{sth_bind_params};
28 1         6 my $query = delete $args{query};
29 1         3 my $table = delete $args{table}; # XXX quote
30 1         3 my $column = delete $args{column}; # XXX quote
31 1 50       5 if (defined $sth) {
32             } else {
33 1 50       5 die "You specify 'query' or 'table' & 'column', but you don't specify ".
34             "dbh/dsn+user+password, so I cannot create a statement handle"
35             unless $dbh;
36 1 50 33     13 if (defined $query) {
    50          
37             } elsif (defined $table && defined $column) {
38 1         4 $query = "SELECT $column FROM $table";
39             } else {
40 0         0 die "Please specify 'sth', 'query', or 'table' & 'column' arguments";
41             }
42 1         15 $sth = $dbh->prepare($query);
43 1   50     125 $sth->execute(@{ $sth_bind_params // [] }); # to check query syntax
  1         87  
44             }
45              
46 1         6 my $row_count_sth = delete $args{row_count_sth};
47 1         3 my $row_count_sth_bind_params = delete $args{row_count_sth_bind_params};
48 1         3 my $row_count_query = delete $args{row_count_query};
49 1 50       5 if (defined $row_count_sth) {
50             } else {
51 1 50       4 die "You specify 'row_count_query' or 'table', but you don't specify ".
52             "dbh/dsn+user+password, so I cannot create a statement handle"
53             unless $dbh;
54 1 50       5 if (defined $row_count_query) {
    50          
55             } elsif (defined $table) {
56 1         3 $row_count_query = "SELECT COUNT(*) FROM $table";
57             } else {
58 0         0 die "For getting row count, please specify 'row_count_sth', ".
59             "'row_count_query', or 'table' argument";
60             }
61 1         8 $row_count_sth = $dbh->prepare($row_count_query);
62 1   50     66 $sth->execute(@{ $row_count_sth_bind_params // [] }); # to check query syntax
  1         60  
63             }
64              
65 1 50       8 die "Unknown argument(s): ". join(", ", sort keys %args)
66             if keys %args;
67              
68 1         12 bless {
69             #dbh => $dbh,
70             sth => $sth,
71             sth_bind_params => $sth_bind_params,
72             row_count_sth => $row_count_sth,
73             row_count_sth_bind_params => $row_count_sth_bind_params,
74             pos => 0, # iterator pos
75             #buf => '', # exists when there is a buffer
76             }, $class;
77             }
78              
79             sub get_next_item {
80 9     9 0 22 my $self = shift;
81 9 100       22 if (exists $self->{buf}) {
82 6         9 $self->{pos}++;
83 6         16 return delete $self->{buf};
84             } else {
85 3         70 my $row = $self->{sth}->fetchrow_arrayref;
86 3 50       22 die "StopIteration" unless $row;
87 3         5 $self->{pos}++;
88 3         24 $row->[0];
89             }
90             }
91              
92             sub has_next_item {
93 8     8 0 13 my $self = shift;
94 8 50       19 if (exists $self->{buf}) {
95 0         0 return 1;
96             }
97 8         65 my $row = $self->{sth}->fetchrow_arrayref;
98 8 100       38 return 0 unless $row;
99 6         17 $self->{buf} = $row->[0];
100 6         16 1;
101             }
102              
103             sub get_item_count {
104 1     1 0 3 my $self = shift;
105 1   50     4 $self->{row_count_sth}->execute(@{ $self->{row_count_sth_bind_params} // [] });
  1         19  
106 1         10 my ($row_count) = $self->{row_count_sth}->fetchrow_array;
107 1         7 $row_count;
108             }
109              
110             sub reset_iterator {
111 4     4 0 13 my $self = shift;
112 4   50     13 $self->{sth}->execute(@{ $self->{sth_bind_params} // [] });
  4         343  
113 4         25 $self->{pos} = 0;
114             }
115              
116             sub get_iterator_pos {
117 0     0 0 0 my $self = shift;
118 0         0 $self->{pos};
119             }
120              
121             sub get_item_at_pos {
122 3     3 0 56 my ($self, $pos) = @_;
123 3 100       13 $self->reset_iterator if $self->{pos} > $pos;
124 3         6 while (1) {
125 5 100       13 die "Out of range" unless $self->has_next_item;
126 4         9 my $item = $self->get_next_item;
127 4 100       19 return $item if $self->{pos} > $pos;
128             }
129             }
130              
131             sub has_item_at_pos {
132 3     3 0 8 my ($self, $pos) = @_;
133 3 100       17 return 1 if $self->{pos} > $pos;
134 2         4 while (1) {
135 3 100       8 return 0 unless $self->has_next_item;
136 2         7 $self->get_next_item;
137 2 100       10 return 1 if $self->{pos} > $pos;
138             }
139             }
140              
141             1;
142             # ABSTRACT: Role to access elements from DBI
143              
144             __END__