File Coverage

blib/lib/Bif/DB.pm
Criterion Covered Total %
statement 22 79 27.8
branch 0 16 0.0
condition 1 16 6.2
subroutine 7 18 38.8
pod 1 1 100.0
total 31 130 23.8


line stmt bran cond sub pod time code
1             package Bif::DB;
2 45     45   812 use strict;
  45         87  
  45         1197  
3 45     45   224 use warnings;
  45         81  
  45         1135  
4 45     45   49748 use DBD::SQLite;
  45         1114751  
  45         1401  
5 45     45   26280 use DBIx::ThinSQL ();
  45         235553  
  45         881  
6 45     45   293 use Carp ();
  45         98  
  45         833  
7 45     45   231 use Log::Any '$log';
  45         101  
  45         341  
8              
9             our $VERSION = '0.1.5_7';
10             our @ISA = ('DBIx::ThinSQL');
11              
12             sub _connected {
13 0     0   0 my $dbh = shift;
14 0         0 my $debug = shift;
15              
16 0 0   0   0 $dbh->sqlite_trace( sub { $log->debug(@_) } ) if $debug;
  0         0  
17              
18             # $dbh->trace('1|SQL',\*STDOUT) if $debug;
19              
20 0         0 $dbh->do('PRAGMA query_only = ON;');
21 0         0 $dbh->do('PRAGMA foreign_keys = ON;');
22 0         0 $dbh->do('PRAGMA temp_store = MEMORY;');
23              
24             # TODO remove before the first production release.
25 0         0 $dbh->do('PRAGMA reverse_unordered_selects = ON;');
26 0         0 return;
27             }
28              
29             sub connect {
30 132     132 1 274 my $class = shift;
31 132         307 my ( $dsn, $username, $password, $attrs, $debug ) = @_;
32              
33             $attrs ||= {
34             RaiseError => 1,
35             PrintError => 0,
36             ShowErrorStatement => 1,
37             sqlite_see_if_its_a_number => 1,
38             sqlite_unicode => 1,
39             Callbacks => {
40 0     0   0 connected => sub { _connected( shift, $debug ) },
41             },
42 132   50     448 };
43              
44 132         978 return $class->SUPER::connect( $dsn, $username, $password, $attrs, );
45             }
46              
47             package Bif::DB::db;
48             DBIx::ThinSQL->import(qw/ qv bv case coalesce/);
49              
50             our @ISA = ('DBIx::ThinSQL::db');
51              
52             sub uuid2id {
53 0     0     my $self = shift;
54 0   0       my $uuid = shift || return;
55              
56 0 0         if ( length($uuid) == 40 ) {
57              
58             # In this case we know we will only get one match back
59              
60 0           return $self->xarrayref(
61             select => [ 'n.id', 'n.uuid', ],
62             from => 'nodes n',
63             where => { 'n.uuid' => $uuid },
64             limit => 1,
65             );
66             }
67              
68 0           return $self->xarrayrefs(
69             select_distinct => [ 'n.id', 'n.uuid AS uuid', ],
70             from => 'nodes n',
71             where => [ 'n.uuid LIKE ', qv( $uuid . '%' ) ],
72             );
73             }
74              
75             sub get_change {
76 0     0     my $self = shift;
77 0   0       my $token = shift || return;
78              
79 0 0         if ( $token =~ m/^(\d+)\.(\d+)$/ ) {
80 0           my $id = $1;
81 0           my $change_id = $2;
82 0           my $data = $self->xhashref(
83             select => [
84             'nodes.id', 'nodes.kind',
85             'nodes.uuid', 'changes.id AS change_id',
86             qv(undef)->as('project_id'),
87             ],
88             from => 'nodes',
89             inner_join => 'changes',
90             on => { 'changes.id' => $change_id },
91             where => { 'nodes.id' => $id },
92             );
93 0           return $data;
94             }
95              
96 0           return;
97             }
98              
99             sub get_local_hub_id {
100 0     0     my $self = shift;
101              
102 0           my $id = $self->xval(
103             select => 'b.hub_id',
104             from => 'bifkv b',
105             where => { 'b.key' => 'local_hub' },
106             );
107              
108 0           return $id;
109             }
110              
111             sub get_projects {
112 0     0     my $self = shift;
113 0   0       my $path = shift || return;
114              
115 0 0         if ( $path =~ m/^\d+$/ ) {
116 0           return $self->xhashrefs(
117             select => [
118             'n.id AS id',
119             'n.kind AS kind',
120             'n.uuid AS uuid',
121             'p.parent_id AS parent_id',
122             'n.path AS path',
123             'n.first_change_id AS first_change_id',
124             'p.default_hub_id AS default_hub_id',
125             'p.local AS local',
126             'h.name AS hub_name',
127             ],
128             from => 'projects p',
129             inner_join => 'nodes n',
130             on => 'n.id = p.id',
131             left_join => 'nodes h',
132             on => 'h.id = p.default_hub_id',
133             where => { 'p.id' => $path },
134             );
135             }
136              
137 0           return $self->xhashrefs(
138             select => [
139             'n.id AS id',
140             'n.kind AS kind',
141             'n.uuid AS uuid',
142             'n.parent_id AS parent_id',
143             'n.path AS path',
144             'n.first_change_id AS first_change_id',
145             'p.default_hub_id AS default_hub_id',
146             'p.local AS local',
147             'h.name AS hub_name',
148             ],
149             from => 'nodes n',
150             inner_join => 'projects p',
151             on => 'p.id = n.id',
152             left_join => 'nodes h',
153             on => 'h.id = p.default_hub_id',
154             where => [ 'n.path = ', qv($path), ' OR n.name = ', qv($path) ],
155             order_by => [qw/n.path/],
156             );
157             }
158              
159             sub status_ids {
160 0     0     my $self = shift;
161 0           my $project_id = shift;
162 0           my $kind = shift;
163              
164 0 0 0       return ( [], [] ) unless $project_id and $kind and @_;
      0        
165              
166 0           my @ids;
167 0 0         my %invalid = map { defined $_ ? ( $_ => 1 ) : () } @_;
  0            
168              
169 0           my @known = $self->xarrayrefs(
170             select => 'id, status',
171             from => $kind . '_status',
172             where => { project_id => $project_id },
173             );
174              
175 0           foreach my $known (@known) {
176             push( @ids, $known->[0] )
177 0 0         if delete $invalid{ $known->[1] };
178             }
179              
180             # sorted keys so we can test
181 0           return \@ids, [ sort keys %invalid ];
182             }
183              
184             sub get_hub {
185 0     0     my $self = shift;
186 0   0       my $hub = shift || return;
187              
188 0           return $self->xhashref(
189             select => [
190             qw/h.id n.name n.kind n.path n.uuid h.location n.first_change_id/,
191             , 'b.hub_id AS is_default'
192             ],
193             from => 'nodes n',
194             inner_join => 'hubs h',
195             on => 'h.id = n.id',
196             inner_join => 'projects p',
197             on => 'p.id = h.id',
198             left_join => 'bifkv b',
199             on => 'b.key = "local" AND b.hub_id = h.id',
200             where => [
201             'n.name = ', qv($hub), ' OR h.location = ', qv($hub),
202             ' OR h.id = ', qv($hub)
203             ],
204             );
205             }
206              
207             sub get_max_change_id {
208 0     0     my $self = shift;
209 0           my $uid = $self->xval(
210             select => ['MAX(c.id)'],
211             from => 'changes c',
212             );
213              
214 0           return $uid;
215              
216             }
217              
218             sub check_fks {
219 0     0     my $self = shift;
220 0           my $sth = $self->table_info( '%', '%', '%' );
221              
222 0           my %seen;
223              
224 0           while ( my $t_info = $sth->fetchrow_hashref('NAME_lc') ) {
225             my $sth2 =
226             $self->foreign_key_info( undef, undef, undef, undef, undef,
227 0           $t_info->{table_name} );
228              
229 0           while ( my $fk_info = $sth2->fetchrow_hashref('NAME_lc') ) {
230              
231             next
232             if $seen{ $fk_info->{fktable_name}
233             . $fk_info->{fkcolumn_name}
234             . $fk_info->{pktable_name}
235 0 0         . $fk_info->{pkcolumn_name} }++;
236              
237             my @missing = $self->xarrayrefs(
238             select_distinct => "$fk_info->{fkcolumn_name} AS col1",
239             from => $fk_info->{fktable_name},
240             where => "$fk_info->{fkcolumn_name} IS NOT NULL",
241             except_select => $fk_info->{pkcolumn_name},
242             from => $fk_info->{pktable_name},
243 0           where => "$fk_info->{pkcolumn_name} IS NOT NULL",
244             );
245              
246             print "$fk_info->{fktable_name}.$fk_info->{fkcolumn_name}: $_->[0]"
247             . " ($fk_info->{pktable_name}.$fk_info->{pkcolumn_name})\n"
248 0           for @missing;
249              
250             }
251             }
252             }
253              
254             package Bif::DB::st;
255             our @ISA = ('DBIx::ThinSQL::st');
256              
257             1;
258              
259             =head1 NAME
260              
261             =for bif-doc #perl
262              
263             Bif::DB - helper methods for a read-only bif database
264              
265             =head1 VERSION
266              
267             0.1.5_7 (2015-11-25)
268              
269             =head1 SYNOPSIS
270              
271             use strict;
272             use warnings;
273             use Bif::DB;
274              
275             # Bif::DB inherits from DBIx::ThinSQL, which inherits from DBI.
276             my $db = Bif::DB->connect( $dsn );
277              
278             # Read only operations on a bif database:
279             my @ids = $db->uuid2id( $uuid );
280              
281             =head1 DESCRIPTION
282              
283             B is a L derivative that provides various read-only
284             methods for retrieving information from a L repository. For a
285             read-write equivalent see L. The read-only and read-write
286             parts are separated for performance reasons.
287              
288             =head1 METHODS
289              
290             =over
291              
292             =item uuid2id( $UUID ) -> List[Int]
293              
294             Returns the (possibly multiple) integer ID(s) matching a node C<$UUID>.
295              
296             =item get_change( "$ID.$UPDATE_ID" ) -> HashRef
297              
298             Looks up the change identified by C<$ID.$UPDATE_ID> and returns undef
299             or a hash reference containg the following keys:
300              
301             =over
302              
303             =item * id - the node ID
304              
305             =item * change_id - the ID of the change
306              
307             =item * kind - the type of the node
308              
309             =item * uuid - the universally unique identifier of the node
310              
311             =back
312              
313             =item get_local_hub_id -> Int
314              
315             Returns the ID for the local repository node.
316              
317             =item get_projects( $PATH ) -> @HashRefs | \@HashRefs
318              
319             Looks up the project(s) identified by C<$PATH> (in [HUB/]PATH format)
320             and returns a list of hash references containg the following keys:
321              
322             =over
323              
324             =item * id - the node ID
325              
326             =item * first_change_id - the change_id that created the node
327              
328             =item * kind - the type of the node
329              
330             =item * uuid - the universally unique identifier of the node
331              
332             =item * path - the path of the project
333              
334             =item * parent_id - the parent ID of the project
335              
336             =item * default_hub_id - the id of the project's default hub
337              
338             =item * hub_name - the name of the project's hub
339              
340             =item * local - true if the project is locally synchronized
341              
342             =back
343              
344             The list is sorted by hub name then by project path.
345              
346             NOTE: The underlying DBIx::ThinSQL method returns an Array References
347             to the list in scalar context.
348              
349             =item status_ids( $project_id, $kind, @status ) -> \@ids, \@invalid
350              
351             Takes a project ID, a thread type (task, issue, etc) and a list of
352             status names and returns an arrayref of matching IDs, and an arrayref
353             of invalid names. This method will silently ignore any @status which
354             are undefined.
355              
356             =item get_hub( $name ) -> @HashRef
357              
358             Returns a HASH reference containing information about the hub
359             identified by C<$name> with the following keys:
360              
361             =over
362              
363             =item * first_change_id - the first change ID
364              
365             =item * id - the node ID for the hub
366              
367             =item * location - the location of the hub
368              
369             =item * name - the name of the hub
370              
371             =item * path - the path of the hub node
372              
373             =item * uuid - the node unique identifier
374              
375             =back
376              
377             Returns C if C<$name> (a name, an ID, or a location) is not
378             found.
379              
380             =item get_max_change_id
381              
382             Returns the maximum change ID in the database.
383              
384             =item check_fks
385              
386             This is developer aide to print out foreign key relationship that are
387             not satisfied (i.e. where the target row/column doesn't exist).
388              
389             =back
390              
391             =head1 SEE ALSO
392              
393             L
394              
395             =head1 AUTHOR
396              
397             Mark Lawrence Enomad@null.netE
398              
399             =head1 COPYRIGHT AND LICENSE
400              
401             Copyright 2013-2015 Mark Lawrence
402              
403             This program is free software; you can redistribute it and/or modify it
404             under the terms of the GNU General Public License as published by the
405             Free Software Foundation; either version 3 of the License, or (at your
406             option) any later version.
407