File Coverage

blib/lib/Graph/Feather.pm
Criterion Covered Total %
statement 255 270 94.4
branch 16 20 80.0
condition 3 9 33.3
subroutine 60 61 98.3
pod 49 49 100.0
total 383 409 93.6


line stmt bran cond sub pod time code
1             package Graph::Feather;
2 3     3   376322 use strict;
  3         26  
  3         90  
3 3     3   15 use warnings;
  3         5  
  3         64  
4 3     3   3833 use DBI;
  3         44484  
  3         167  
5 3     3   2156 use DBD::SQLite;
  3         23256  
  3         91  
6 3     3   1069 use version;
  3         4694  
  3         15  
7              
8             our $VERSION = '0.09';
9              
10             our $VERSION_INT =
11             1_000_000 * version->parse($VERSION)->numify;
12              
13             # Archive::Zip crc32 of 'Graph::Feather'
14             our $APPLICATION_ID = -801604570;
15              
16             sub new {
17 1002     1002 1 4546037 my ($class, %options) = @_;
18              
19 1002         3015 my $self = bless {
20             }, $class;
21              
22 1002         2506 my $db_file = ':memory:';
23              
24 1002         7426 $self->{dbh} = DBI
25             ->connect("dbi:SQLite:dbname=$db_file", "", "");
26              
27 1002         423505 local $self->{dbh}->{sqlite_allow_multiple_statements} = 1;
28              
29             ###################################################################
30             # Deploy DB schema
31              
32 1002         22979 $self->{dbh}->do(sprintf q{
33             PRAGMA application_id = %i;
34             PRAGMA user_version = %u;
35             }, $APPLICATION_ID, $VERSION_INT);
36              
37 1002         68543 $self->{dbh}->do(q{
38              
39             -----------------------------------------------------------------
40             -- Pragmata
41             -----------------------------------------------------------------
42              
43             PRAGMA foreign_keys = ON;
44             PRAGMA synchronous = OFF;
45             PRAGMA journal_mode = OFF;
46             PRAGMA locking_mode = EXCLUSIVE;
47              
48             -----------------------------------------------------------------
49             -- Graph
50             -----------------------------------------------------------------
51              
52             CREATE TABLE Graph_Attribute(
53             attribute_name UNIQUE NOT NULL,
54             attribute_value
55             );
56              
57             -----------------------------------------------------------------
58             -- Vertices
59             -----------------------------------------------------------------
60              
61             CREATE TABLE Vertex(
62             vertex_name UNIQUE NOT NULL
63             );
64              
65             CREATE TABLE Vertex_Attribute(
66             vertex NOT NULL,
67             attribute_name NOT NULL,
68             attribute_value,
69             UNIQUE(vertex, attribute_name),
70             FOREIGN KEY (vertex)
71             REFERENCES Vertex(vertex_name)
72             ON DELETE CASCADE
73             ON UPDATE CASCADE
74             );
75              
76             -----------------------------------------------------------------
77             -- Edges
78             -----------------------------------------------------------------
79              
80             CREATE TABLE Edge(
81             src NOT NULL,
82             dst NOT NULL,
83             UNIQUE(src, dst),
84             FOREIGN KEY (src)
85             REFERENCES Vertex(vertex_name)
86             ON DELETE CASCADE
87             ON UPDATE CASCADE
88             FOREIGN KEY (dst)
89             REFERENCES Vertex(vertex_name)
90             ON DELETE CASCADE
91             ON UPDATE CASCADE
92             );
93              
94             -- Can use covering index instead
95             -- CREATE INDEX idx_Edge_src
96             -- ON Edge (src);
97              
98             CREATE INDEX idx_Edge_dst
99             ON Edge (dst);
100              
101             CREATE TABLE Edge_Attribute(
102             src NOT NULL,
103             dst NOT NULL,
104             attribute_name NOT NULL,
105             attribute_value,
106             UNIQUE(src, dst, attribute_name),
107             FOREIGN KEY (src, dst)
108             REFERENCES Edge(src, dst)
109             ON DELETE CASCADE
110             ON UPDATE CASCADE
111             );
112              
113             -----------------------------------------------------------------
114             -- Triggers that add vertices and edges when needed elsewhere
115             -----------------------------------------------------------------
116              
117             CREATE TRIGGER trigger_Edge_insert
118             BEFORE INSERT ON Edge
119             BEGIN
120             INSERT OR IGNORE
121             INTO Vertex(vertex_name)
122             VALUES(NEW.src);
123              
124             INSERT OR IGNORE
125             INTO Vertex(vertex_name)
126             VALUES(NEW.dst);
127             END;
128              
129             CREATE TRIGGER trigger_Vertex_Attribute_insert
130             BEFORE INSERT ON Vertex_Attribute
131             BEGIN
132             INSERT OR IGNORE
133             INTO Vertex(vertex_name)
134             VALUES(NEW.vertex);
135             END;
136              
137             CREATE TRIGGER trigger_Edge_Attribute_insert
138             BEFORE INSERT ON Edge_Attribute
139             BEGIN
140             INSERT OR IGNORE
141             INTO Edge(src, dst)
142             VALUES(NEW.src, NEW.dst);
143             END;
144              
145             });
146              
147             ###################################################################
148             # Register hook to synchronise attribute Perl objects
149              
150             $self->{dbh}->sqlite_update_hook(sub {
151 57886 100   57886   510809 return unless $_[0] == 9; # DBD::SQLite::DELETE
152 10872         125488 delete $self->{ $_[2] }{ $_[3] };
153 1002         857829 });
154              
155             ###################################################################
156             # Process options
157              
158 1001         4951 $self->add_vertices(@{ $options{vertices} })
159 1002 100       4139 if exists $options{vertices};
160              
161 1001         4989 $self->add_edges(@{ $options{edges} })
162 1002 100       3597 if exists $options{edges};
163              
164 1002         9961 return $self;
165             };
166              
167             #####################################################################
168             # DB helpers
169             #####################################################################
170              
171             sub _prepare {
172 33652     33652   62250 my ($self, $statement, $attr, @other) = @_;
173              
174             # TODO(bh): As of 2018-02 effect/purpose of %attr seems
175             # undefined in DBI documentation. So we fail on them here.
176 33652 50 33     124979 if ($attr or @other) {
177             ...
178 0         0 }
179              
180 33652   66     177451 $self->{cache}{$statement} //= $self->{dbh}->prepare($statement);
181 33652         1433082 return $self->{cache}{$statement};
182             }
183              
184             #####################################################################
185             # Basics
186             #####################################################################
187              
188             sub add_vertex {
189 1013     1013 1 314391 my ($self, $v) = @_;
190 1013         3207 add_vertices($self, $v);
191             }
192              
193             sub add_edge {
194 1066     1066 1 410319 my ($self, $src, $dst) = @_;
195 1066         3948 add_edges($self, [$src, $dst]);
196             }
197              
198             sub has_vertex {
199 1036     1036 1 266429 my ($self, $v) = @_;
200              
201 1036         2609 my $sth = $self->_prepare(q{
202             SELECT 1 FROM Vertex WHERE vertex_name = ?
203             });
204              
205 1036         14837 my ($result) = $self->{dbh}->selectrow_array($sth, {}, $v);
206              
207 1036         5069 return !!$result;
208             }
209              
210             sub has_edge {
211 547     547 1 343524 my ($self, $src, $dst) = @_;
212            
213 547         1648 my $sth = $self->_prepare(q{
214             SELECT 1 FROM Edge WHERE src = ? AND dst = ?
215             });
216              
217 547         8816 my ($result) = $self->{dbh}->selectrow_array($sth, {}, $src, $dst);
218              
219 547         2935 return !!$result;
220             }
221              
222             sub delete_vertex {
223 529     529 1 465289 my ($self, $v) = @_;
224              
225 529         2123 delete_vertices($self, $v);
226             }
227              
228             sub delete_vertices {
229 1101     1101 1 1069047 my ($self, @vertices) = @_;
230            
231             # delete_vertex_attributes($self, $_) for @vertices;
232              
233 1101         2881 my $sth = $self->_prepare(q{
234             DELETE FROM Vertex WHERE vertex_name = ?
235             });
236              
237 1101         6314 $self->{dbh}->begin_work();
238 1101         45278 $sth->execute($_) for @vertices;
239 1101         5169 $sth->finish();
240 1101         13187 $self->{dbh}->commit();
241             }
242              
243             sub delete_edge {
244 558     558 1 341417 my ($self, $src, $dst) = @_;
245 558         2312 feather_delete_edges($self, [$src, $dst]);
246             }
247              
248             sub delete_edges {
249 283     283 1 231418 my ($self, @vertices) = @_;
250 283         608 my @edges;
251              
252 283         1086 while (@vertices) {
253 829         1691 my ($src, $dst) = splice @vertices, 0, 2;
254 829 50       1806 warn 'delete_edges takes vertex list, not list of vertex pairs'
255             if ref $src;
256 829         1965 push @edges, [ $src, $dst ];
257             }
258              
259 283         1055 $self->feather_delete_edges(@edges);
260             }
261              
262             sub feather_delete_edges {
263 841     841 1 2155 my ($self, @edges) = @_;
264            
265 841         3089 delete_edge_attributes($self, @$_) for @edges;
266              
267 841         1984 my $sth = $self->_prepare(q{
268             DELETE FROM Edge WHERE src = ? AND dst = ?
269             });
270              
271 841         4679 $self->{dbh}->begin_work();
272 841         29335 $sth->execute(@$_) for @edges;
273 841         3064 $sth->finish();
274 841         8569 $self->{dbh}->commit();
275             }
276              
277             #####################################################################
278             # Mutators
279             #####################################################################
280              
281             sub add_vertices {
282 2561     2561 1 437415 my ($self, @vertices) = @_;
283              
284 2561         6051 my $sth = $self->_prepare(q{
285             INSERT OR IGNORE INTO Vertex(vertex_name) VALUES (?)
286             });
287              
288 2561         13363 $self->{dbh}->begin_work();
289 2561         87900 $sth->execute($_) for @vertices;
290 2561         9162 $sth->finish();
291 2561         21869 $self->{dbh}->commit;
292             }
293              
294             sub add_edges {
295 2583     2583 1 765842 my ($self, @edges) = @_;
296              
297 2583         7178 my $sth = $self->_prepare(q{
298             INSERT OR IGNORE INTO Edge(src, dst) VALUES (?, ?)
299             });
300              
301 2583         13887 $self->{dbh}->begin_work();
302 2583         113908 $sth->execute(@$_) for @edges;
303 2583         9188 $sth->finish();
304 2583         25732 $self->{dbh}->commit();
305             }
306              
307             #####################################################################
308             # Accessors
309             #####################################################################
310              
311             sub vertices {
312 547     547 1 356343 my ($self) = @_;
313              
314 547         1664 my $sth = $self->_prepare(q{
315             SELECT vertex_name FROM Vertex
316             });
317              
318 547         2992 return map { @$_ } $self->{dbh}->selectall_array($sth);
  4793         20260  
319             }
320              
321             sub edges {
322 549     549 1 623921 my ($self) = @_;
323              
324 549         1709 my $sth = $self->_prepare(q{
325             SELECT src, dst FROM Edge
326             });
327              
328 549         3134 return $self->{dbh}->selectall_array($sth);
329             }
330              
331             sub successors {
332 537     537 1 406875 my ($self, $v) = @_;
333              
334 537         1588 my $sth = $self->_prepare(q{
335             SELECT dst FROM Edge WHERE src = ?
336             });
337              
338 537         3492 return map { @$_ } $self->{dbh}->selectall_array($sth, {}, $v);
  944         8454  
339             }
340              
341             sub all_successors {
342 550     550 1 653801 my ($self, $v) = @_;
343              
344 550         1692 my $sth = $self->_prepare(q{
345             WITH RECURSIVE all_successors(v) AS (
346             SELECT dst FROM Edge WHERE src = ?
347            
348             UNION
349            
350             SELECT dst
351             FROM Edge
352             INNER JOIN all_successors
353             ON (Edge.src = all_successors.v)
354             )
355             SELECT v FROM all_successors
356             });
357              
358 550         3426 return map { @$_ } $self->{dbh}->selectall_array($sth, {}, $v);
  2121         63344  
359             }
360              
361             sub successorless_vertices {
362 531     531 1 700485 my ($self) = @_;
363              
364 531         2070 my $sth = $self->_prepare(q{
365             SELECT vertex_name
366             FROM Vertex
367             LEFT JOIN Edge
368             ON (Vertex.vertex_name = Edge.src)
369             WHERE Edge.dst IS NULL
370             });
371              
372 531         3031 return map { @$_ } $self->{dbh}->selectall_array($sth);
  1099         13632  
373             }
374              
375             sub predecessors {
376 527     527 1 431638 my ($self, $v) = @_;
377              
378 527         1893 my $sth = $self->_prepare(q{
379             SELECT src FROM Edge WHERE dst = ?
380             });
381              
382 527         3270 return map { @$_ } $self->{dbh}->selectall_array($sth, {}, $v);
  960         8751  
383             }
384              
385             sub all_predecessors {
386 518     518 1 596568 my ($self, $v) = @_;
387              
388 518         1560 my $sth = $self->_prepare(q{
389             WITH RECURSIVE all_predecessors(v) AS (
390             SELECT src FROM Edge WHERE dst = ?
391            
392             UNION
393            
394             SELECT src
395             FROM Edge
396             INNER JOIN all_predecessors
397             ON (Edge.dst = all_predecessors.v)
398             )
399             SELECT v FROM all_predecessors
400             });
401              
402 518         3242 return map { @$_ } $self->{dbh}->selectall_array($sth, {}, $v);
  1993         60900  
403             }
404              
405             sub predecessorless_vertices {
406 523     523 1 678950 my ($self) = @_;
407              
408 523         1703 my $sth = $self->_prepare(q{
409             SELECT vertex_name
410             FROM Vertex
411             LEFT JOIN Edge
412             ON (Vertex.vertex_name = Edge.dst)
413             WHERE Edge.src IS NULL
414             });
415              
416 523         2985 return map { @$_ } $self->{dbh}->selectall_array($sth);
  1088         14574  
417             }
418              
419             #####################################################################
420             # Degree
421             #####################################################################
422              
423             sub edges_at {
424 519     519 1 405512 my ($self, $v) = @_;
425              
426 519         3543 return $self->{dbh}->selectall_array(q{
427             SELECT src, dst
428             FROM Edge
429             WHERE (?) IN (src, dst)
430             }, {}, $v);
431             }
432              
433             sub edges_to {
434 521     521 1 439235 my ($self, $v) = @_;
435              
436 521         1617 my $sth = $self->_prepare(q{
437             SELECT src, dst
438             FROM Edge
439             WHERE dst = ?
440             });
441              
442 521         3254 return $self->{dbh}->selectall_array($sth, {}, $v);
443             }
444              
445             sub edges_from {
446 523     523 1 423693 my ($self, $v) = @_;
447              
448 523         1658 my $sth = $self->_prepare(q{
449             SELECT src, dst
450             FROM Edge
451             WHERE src = ?
452             });
453              
454 523         3152 return $self->{dbh}->selectall_array($sth, {}, $v);
455             }
456              
457             #####################################################################
458             # Attributes
459             #####################################################################
460              
461             #####################################################################
462             # Vertex Attributes
463             #####################################################################
464              
465             sub set_vertex_attribute {
466 1208     1208 1 435987 my ($self, $v, $name, $value) = @_;
467              
468 1208         4037 delete_vertex_attribute($self, $v, $name);
469              
470 1208         3016 my $sth = $self->_prepare(q{
471             INSERT INTO Vertex_Attribute(
472             vertex, attribute_name, attribute_value
473             )
474             VALUES (?, ?, ?)
475             });
476              
477 1208         23041 $sth->execute($v, $name, $value);
478              
479 1208         5404 my $id = $self->{dbh}->sqlite_last_insert_rowid();
480              
481 1208         3143 $sth->finish();
482              
483 1208         7014 $self->{Vertex_Attribute}{ $id } = $value;
484             }
485              
486             sub _get_vertex_attribute_value_id {
487 1046     1046   2513 my ($self, $v, $name) = @_;
488              
489 1046         2559 my $sth = $self->_prepare(q{
490             SELECT rowid
491             FROM Vertex_Attribute
492             WHERE vertex = ?
493             AND attribute_name = ?
494             });
495              
496 1046         16843 my ($rowid) = $self->{dbh}->selectrow_array($sth, {}, $v, $name);
497              
498 1046         3459 return $rowid;
499             }
500              
501             sub get_vertex_attribute {
502 515     515 1 432583 my ($self, $v, $name) = @_;
503              
504 515         1752 my $rowid = _get_vertex_attribute_value_id($self, $v, $name);
505 515 100       2451 return unless defined $rowid;
506 9         41 return $self->{Vertex_Attribute}{ $rowid };
507             }
508              
509             sub set_vertex_attributes {
510 539     539 1 468107 my ($self, $v, $attr) = @_;
511              
512 539         2229 $self->delete_vertex_attributes($v);
513 539         2153 $self->add_vertex($v);
514             $self->set_vertex_attribute($v, $_, $attr->{$_})
515 539         3791 for keys %$attr;
516             }
517              
518             sub get_vertex_attributes {
519 530     530 1 408561 my ($self, $v) = @_;
520              
521 530         1600 my $sth = $self->_prepare(q{
522             SELECT
523             rowid,
524             attribute_name
525             FROM Vertex_Attribute
526             WHERE vertex = ?
527             });
528              
529 530         1049 my @result;
530              
531 530         2934 for ($self->{dbh}->selectall_array($sth, {}, $v)) {
532             push @result,
533             $_->[1],
534 35         875 $self->{Vertex_Attribute}{ $_->[0] };
535             }
536              
537 530 100       10854 return undef unless $self->has_vertex($v);
538 315         1151 return { @result };
539             }
540              
541             sub has_vertex_attribute {
542 531     531 1 389940 my ($self, $v, $name) = @_;
543 531         1958 my $rowid = _get_vertex_attribute_value_id($self, $v, $name);
544 531         2107 return defined $rowid;
545             }
546              
547             sub delete_vertex_attribute {
548 1710     1710 1 358193 my ($self, $v, $name) = @_;
549              
550 1710         4182 my $sth = $self->_prepare(q{
551             DELETE
552             FROM Vertex_Attribute
553             WHERE vertex = ?
554             AND attribute_name = ?
555             });
556              
557 1710         25513 $sth->execute($v, $name);
558 1710         7658 $sth->finish();
559             }
560              
561             sub get_vertex_attribute_names {
562 528     528 1 378806 my ($self, $v) = @_;
563              
564 528         1761 my $sth = $self->_prepare(q{
565             SELECT attribute_name
566             FROM Vertex_Attribute
567             WHERE vertex = ?
568             });
569              
570 528         3155 return map { @$_ } $self->{dbh}->selectall_array($sth, {}, $v);
  27         461  
571             }
572              
573             sub delete_vertex_attributes {
574 1073     1073 1 377526 my ($self, $v) = @_;
575              
576 1073         2838 my $sth = $self->_prepare(q{
577             DELETE
578             FROM Vertex_Attribute
579             WHERE vertex = ?
580             });
581              
582 1073         19539 $sth->execute($v);
583 1073         5880 $sth->finish();
584             }
585              
586             #####################################################################
587             # Edge Attributes
588             #####################################################################
589              
590             sub set_edge_attribute {
591 1208     1208 1 1621912 my ($self, $src, $dst, $name, $value) = @_;
592              
593 1208         4490 delete_edge_attribute($self, $src, $dst, $name);
594              
595 1208         2506 my $sth = $self->_prepare(q{
596             INSERT INTO Edge_Attribute(
597             src, dst, attribute_name, attribute_value
598             )
599             VALUES (?, ?, ?, ?)
600             });
601              
602 1208         30456 $sth->execute($src, $dst, $name, $value);
603              
604 1208         5882 my $id = $self->{dbh}->sqlite_last_insert_rowid();
605              
606 1208         3474 $sth->finish();
607              
608 1208         7093 $self->{Edge_Attribute}{ $id } = $value;
609             }
610              
611             sub _get_edge_attribute_value_id {
612 1035     1035   3117 my ($self, $src, $dst, $name) = @_;
613              
614 1035         2528 my $sth = $self->_prepare(q{
615             SELECT rowid
616             FROM Edge_Attribute
617             WHERE src = ?
618             AND dst = ?
619             AND attribute_name = ?
620             });
621              
622 1035         18115 my ($rowid) = $self->{dbh}->selectrow_array($sth,
623             {}, $src, $dst, $name);
624              
625 1035         3469 return $rowid;
626             }
627              
628             sub get_edge_attribute {
629 516     516 1 665366 my ($self, $src, $dst, $name) = @_;
630              
631 516         2022 my $rowid = _get_edge_attribute_value_id($self, $src, $dst, $name);
632 516 100       2953 return unless defined $rowid;
633 3         21 return $self->{Edge_Attribute}{ $rowid };
634             }
635              
636             sub get_edge_attributes {
637 529     529 1 567060 my ($self, $src, $dst) = @_;
638              
639 529         1694 my $sth = $self->_prepare(q{
640             SELECT
641             rowid,
642             attribute_name
643             FROM Edge_Attribute
644             WHERE src = ?
645             AND dst = ?
646             });
647              
648 529         1049 my @result;
649              
650 529         3187 for ($self->{dbh}->selectall_array($sth, {}, $src, $dst)) {
651             push @result,
652             $_->[1],
653 3         86 $self->{Edge_Attribute}{ $_->[0] };
654             }
655            
656 529         11653 return { @result };
657             }
658              
659             sub has_edge_attribute {
660 519     519 1 607288 my ($self, $src, $dst, $name) = @_;
661 519         1797 my $rowid = _get_edge_attribute_value_id($self, $src, $dst, $name);
662 519         2169 return defined $rowid;
663             }
664              
665             sub set_edge_attributes {
666 513     513 1 1429857 my ($self, $src, $dst, $attr) = @_;
667              
668 513         1980 $self->delete_edge_attributes($src, $dst);
669 513         2242 $self->add_edge($src, $dst);
670             $self->set_edge_attribute($src, $dst, $_, $attr->{$_})
671 513         3860 for keys %$attr;
672             }
673              
674             sub delete_edge_attribute {
675 1721     1721 1 529702 my ($self, $src, $dst, $name) = @_;
676              
677 1721         4637 my $sth = $self->_prepare(q{
678             DELETE
679             FROM Edge_Attribute
680             WHERE src = ?
681             AND dst = ?
682             AND attribute_name = ?
683             });
684              
685 1721         27614 $sth->execute($src, $dst, $name);
686 1721         7540 $sth->finish();
687             }
688              
689             sub get_edge_attribute_names {
690 540     540 1 590711 my ($self, $src, $dst) = @_;
691              
692 540         1873 my $sth = $self->_prepare(q{
693             SELECT attribute_name
694             FROM Edge_Attribute
695             WHERE src = ?
696             AND dst = ?
697             });
698              
699 540         3460 return map { @$_ } $self->{dbh}->selectall_array($sth,
  2         48  
700             {}, $src, $dst);
701             }
702              
703             sub delete_edge_attributes {
704 2436     2436 1 484050 my ($self, $src, $dst) = @_;
705              
706 2436         5619 my $sth = $self->_prepare(q{
707             DELETE
708             FROM Edge_Attribute
709             WHERE src = ?
710             AND dst = ?
711             });
712              
713 2436         39832 $sth->execute($src, $dst);
714 2436         11702 $sth->finish();
715             }
716              
717             #####################################################################
718             # Graph Attributes
719             #####################################################################
720              
721             sub set_graph_attribute {
722 1201     1201 1 289995 my ($self, $name, $value) = @_;
723              
724 1201         3597 delete_graph_attribute($self, $name);
725              
726 1201         3080 my $sth = $self->_prepare(q{
727             INSERT INTO Graph_Attribute(
728             attribute_name, attribute_value
729             )
730             VALUES (?, ?)
731             });
732              
733 1201         14903 $sth->execute($name, $value);
734              
735 1201         5107 my $id = $self->{dbh}->sqlite_last_insert_rowid();
736              
737 1201         3324 $sth->finish();
738              
739 1201         6324 $self->{Graph_Attribute}{ $id } = $value;
740             }
741              
742             sub _get_graph_attribute_value_id {
743 1056     1056   2524 my ($self, $name) = @_;
744              
745 1056         2588 my $sth = $self->_prepare(q{
746             SELECT rowid
747             FROM Graph_Attribute
748             WHERE attribute_name = ?
749             });
750              
751 1056         16148 my ($rowid) = $self->{dbh}->selectrow_array($sth, {}, $name);
752              
753 1056         3386 return $rowid;
754             }
755              
756             sub get_graph_attribute {
757 529     529 1 259096 my ($self, $name) = @_;
758              
759 529         1845 my $rowid = _get_graph_attribute_value_id($self, $name);
760 529 100       2468 return unless defined $rowid;
761 66         333 return $self->{Graph_Attribute}{ $rowid };
762             }
763              
764             sub get_graph_attributes {
765 508     508 1 266506 my ($self) = @_;
766              
767 508         1640 my $sth = $self->_prepare(q{
768             SELECT
769             rowid,
770             attribute_name
771             FROM Graph_Attribute
772             });
773              
774 508         996 my @result;
775              
776 508         2794 for ($self->{dbh}->selectall_array($sth, {})) {
777             push @result,
778             $_->[1],
779 251         4590 $self->{Graph_Attribute}{ $_->[0] };
780             }
781            
782 508         6864 return { @result };
783             }
784              
785             sub has_graph_attribute {
786 527     527 1 309959 my ($self, $name) = @_;
787 527         1474 my $rowid = _get_graph_attribute_value_id($self, $name);
788 527         2626 return defined $rowid;
789             }
790              
791             sub set_graph_attributes {
792 514     514 1 305020 my ($self, $attr) = @_;
793              
794 514         1619 $self->delete_graph_attributes();
795             $self->set_graph_attribute($_, $attr->{$_})
796 514         3070 for keys %$attr;
797             }
798              
799             sub delete_graph_attribute {
800 1769     1769 1 289873 my ($self, $name) = @_;
801              
802 1769         4649 my $sth = $self->_prepare(q{
803             DELETE
804             FROM Graph_Attribute
805             WHERE attribute_name = ?
806             });
807              
808 1769         25176 $sth->execute($name);
809 1769         7975 $sth->finish();
810             }
811              
812             sub get_graph_attribute_names {
813 536     536 1 294843 my ($self) = @_;
814              
815 536         1563 my $sth = $self->_prepare(q{
816             SELECT attribute_name
817             FROM Graph_Attribute
818             });
819              
820 536         2786 return map { @$_ } $self->{dbh}->selectall_array($sth);
  255         4339  
821             }
822              
823             sub delete_graph_attributes {
824 1023     1023 1 273692 my ($self) = @_;
825              
826 1023         2573 my $sth = $self->_prepare(q{
827             DELETE
828             FROM Graph_Attribute
829             });
830              
831 1023         13129 $sth->execute();
832 1023         4687 $sth->finish();
833             }
834              
835             #####################################################################
836             # Extensions not provided by Graph::Directed
837             #####################################################################
838              
839             sub _copy_vertices_edges_attributes {
840 2     2   4 my ($lhs, $rhs) = @_;
841              
842             # copy vertices
843 2         10 $rhs->add_vertices($lhs->vertices);
844              
845             # copy edges
846 2         100 $rhs->add_edges($lhs->edges);
847              
848             # TODO(bh): use new plural methods instead
849              
850             # copy graph attributes
851 2         127 for my $n ($lhs->get_graph_attribute_names) {
852 0         0 $rhs->set_graph_attribute($n,
853             $lhs->get_graph_attribute($n));
854             }
855              
856             # copy vertex attributes
857 2         29 for my $v ($lhs->vertices) {
858 4         243 for my $n ($lhs->get_vertex_attribute_names($v)) {
859 0         0 $rhs->set_vertex_attribute($v, $n,
860             $lhs->get_vertex_attribute($v, $n));
861             }
862             }
863              
864             # copy edge attributes
865 2         97 for my $e ($lhs->edges) {
866 2         53 my ($src, $dst) = @$e;
867 2         9 for my $n ($lhs->get_edge_attribute_names($src, $dst)) {
868 2         315 $rhs->set_edge_attribute($src, $dst, $n,
869             $lhs->get_edge_attribute($src, $dst, $n));
870             }
871             }
872             }
873              
874             sub feather_export_to {
875 1     1 1 275 my ($self, $target) = @_;
876 1         4 _copy_vertices_edges_attributes($self, $target);
877             }
878              
879             sub feather_import_from {
880 1     1 1 6 my ($self, $source) = @_;
881 1         3 _copy_vertices_edges_attributes($source, $self);
882             }
883              
884             sub _feather_restore_from_file {
885 0     0     my ($self, $path) = @_;
886              
887 0           $self->{dbh}->begin_work();
888              
889 0           $self->{dbh}->sqlite_backup_from_file($path);
890              
891 0           my $sth1 = $self->_prepare(q{
892             PRAGMA application_id
893             });
894              
895 0           my ($application_id) = $self->{dbh}->selectrow_array($sth1);
896              
897 0           my $sth2 = $self->_prepare(q{
898             PRAGMA user_version
899             });
900              
901 0           my ($user_version) = $self->{dbh}->selectrow_array($sth2);
902              
903 0 0 0       die "$path was not created by this version of Graph::Feather"
904             unless $application_id eq $APPLICATION_ID
905             and $user_version eq $VERSION_INT;
906              
907 0           $self->{dbh}->commit();
908              
909             # The following is needed because attribute values have a
910             # dual life to support Perl references as attribute values.
911              
912 0           $self->set_graph_attribute(@$_) for
913             $self->{dbh}->selectall_array(q{
914             SELECT attribute_name, attribute_value
915             FROM Graph_Attribute});
916              
917 0           $self->set_vertex_attribute(@$_) for
918             $self->{dbh}->selectall_array(q{
919             SELECT vertex, attribute_name, attribute_value
920             FROM Vertex_Attribute});
921              
922 0           $self->set_edge_attribute(@$_) for
923             $self->{dbh}->selectall_array(q{
924             SELECT src, dst, attribute_name, attribute_value
925             FROM Edge_Attribute});
926             }
927              
928             1;
929              
930             __END__