File Coverage

blib/lib/DBD/XMLSimple.pm
Criterion Covered Total %
statement 34 49 69.3
branch 0 8 0.0
condition n/a
subroutine 12 17 70.5
pod n/a
total 46 74 62.1


line stmt bran cond sub pod time code
1             package DBD::XMLSimple;
2              
3 1     1   81236 use warnings;
  1         3  
  1         32  
4 1     1   5 use strict;
  1         1  
  1         29  
5              
6             =head1 NAME
7              
8             DBD::XMLSimple - Access XML data via the DBI interface
9              
10             =head1 VERSION
11              
12             Version 0.06
13              
14             =cut
15              
16             =head1 SYNOPSIS
17              
18             Reads XML and makes it available via DBI.
19              
20             Sadly DBD::AnyData doesn't work with the latest DBI and DBD::AnyData2 isn't
21             out yet, so I am writing this pending the publication of DBD::AnyData2
22              
23             DBD-XMLSimple doesn't yet expect to support complex XML data, so that's why
24             it's not called DBD-XML.
25              
26             use FindBin qw($Bin);
27             use DBI;
28              
29             my $dbh = DBI->connect('dbi:XMLSimple(RaiseError => 1):');
30              
31             $dbh->func('person', 'XML', "$Bin/../data/person.xml", 'xmlsimple_import');
32              
33             my $sth = $dbh->prepare("SELECT * FROM person");
34              
35             Input data will be something like this:
36              
37            
38            
39            
40             Nigel Horne
41             njh@bandsman.co.uk
42            
43            
44             A N Other
45             nobody@example.com
46            
47            
48              
49             If a leaf appears twice it will be concatenated
50              
51            
52            
53            
54             Nigel Horne
55             njh@bandsman.co.uk
56             nhorne@pause.org
57            
58            
59              
60             $sth = $dbh->prepare("Select email FROM person");
61             $sth->execute();
62             $sth->dump_results();
63              
64             Gives the output "njh@bandsman.co.uk,nhorne@pause.org"
65             =cut
66              
67             =head1 SUBROUTINES/METHODS
68              
69             =head2 driver
70              
71             No routines in this module should be called directly by the application.
72              
73             =cut
74              
75 1     1   5 use base qw(DBI::DBD::SqlEngine);
  1         4  
  1         424  
76              
77 1     1   121571 use vars qw($VERSION $drh $methods_already_installed);
  1         2  
  1         160  
78              
79             our $VERSION = '0.06';
80             our $drh = undef;
81             our $methods_already_installed = 0;
82              
83             sub driver
84             {
85 0 0   0     return $drh if $drh;
86              
87 0           my($class, $attr) = @_;
88              
89             # $class .= '::dr';
90             # $drh = DBI::_new_drh($class, {
91             # $drh = DBI::_new_drh("$class::dr", {
92 0           $drh = $class->SUPER::driver({
93             'Name' => 'XMLSimple',
94             'Version' => $VERSION,
95             'Attribution' => 'DBD::XMLSimple by Nigel Horne',
96             });
97              
98 0 0         if($drh) {
99 0 0         unless($methods_already_installed++) {
100             # DBI->setup_driver(__PACKAGE__);
101 0           DBD::XMLSimple::db->install_method('xmlsimple_import');
102             }
103             }
104              
105 0           return $drh;
106             }
107              
108             sub CLONE
109             {
110 0     0     undef $drh;
111             }
112              
113             package DBD::XMLSimple::dr;
114              
115 1     1   6 use vars qw($imp_data_size);
  1         2  
  1         68  
116              
117             sub disconnect_all
118             {
119 0     0     shift->{tables} = {};
120             }
121              
122             sub DESTROY
123             {
124 0     0     shift->{tables} = {};
125             }
126              
127             package DBD::XMLSimple::db;
128              
129 1     1   5 use vars qw($imp_data_size);
  1         2  
  1         87  
130              
131             $DBD::XMLSimple::db::imp_data_size = 0;
132             @DBD::XMLSimple::db::ISA = qw(DBI::DBD::SqlEngine::db);
133              
134             sub xmlsimple_import
135             {
136 0     0     my($dbh, $table_name, $format, $filename, $flags) = @_;
137              
138 0 0         die if($format ne 'XML');
139              
140 0           $dbh->{filename} = $filename;
141             }
142              
143             package DBD::XMLSimple::st;
144              
145 1     1   6 use strict;
  1         2  
  1         21  
146 1     1   4 use warnings;
  1         1  
  1         28  
147              
148 1     1   4 use vars qw($imp_data_size);
  1         2  
  1         49  
149              
150             $DBD::XMLSimple::st::imp_data_size = 0;
151             @DBD::XMLSimple::st::ISA = qw(DBI::DBD::SqlEngine::st);
152              
153             package DBD::XMLSimple::Statement;
154              
155 1     1   5 use strict;
  1         1  
  1         16  
156 1     1   3 use warnings;
  1         2  
  1         22  
157 1     1   1421 use XML::Twig;
  0            
  0            
158             use Carp;
159              
160             @DBD::XMLSimple::Statement::ISA = qw(DBI::DBD::SqlEngine::Statement);
161              
162             sub open_table($$$$$)
163             {
164             my($self, $data, $tname, $createMode, $lockMode) = @_;
165             my $dbh = $data->{Database};
166              
167             my $twig = XML::Twig->new();
168             my $source = $dbh->{filename};
169             if(ref($source) eq 'ARRAY') {
170             $twig->parse(join('', @{$source}));
171             } else {
172             $twig->parsefile($source);
173             }
174              
175             my $root = $twig->root;
176             my %table;
177             my $rows = 0;
178             my %col_nums;
179             my @col_names;
180             foreach my $record($root->children()) {
181             my %row;
182             my $index = 0;
183             foreach my $leaf($record->children) {
184             my $key = $leaf->name();
185             $row{$key} .= ',' if($row{$key});
186             $row{$key} .= $leaf->field();
187             if(!exists($col_nums{$key})) {
188             $col_nums{$key} = $index++;
189             push @col_names, $key;
190             }
191             }
192             $table{data}->{$record->att('id')} = \%row;
193             $rows++;
194             }
195              
196             carp "No data found to import" if($rows == 0);
197             carp "Can't determine column names" if(scalar(@col_names) == 0);
198              
199             $data->{'rows'} = $rows;
200              
201             $table{'table_name'} = $tname;
202             $table{'col_names'} = \@col_names;
203             $table{'col_nums'} = \%col_nums;
204              
205             return DBD::XMLSimple::Table->new($data, \%table);
206             }
207              
208             package DBD::XMLSimple::Table;
209              
210             use strict;
211             use warnings;
212              
213             @DBD::XMLSimple::Table::ISA = qw(DBI::DBD::SqlEngine::Table);
214              
215             sub new
216             {
217             my($class, $data, $attr, $flags) = @_;
218              
219             $attr->{table} = $data;
220             $attr->{readonly} = 1;
221             $attr->{cursor} = 0;
222              
223             my $rc = $class->SUPER::new($data, $attr, $flags);
224              
225             $rc->{col_names} = $attr->{col_names};
226             $rc->{col_nums} = $attr->{col_nums};
227             return $rc;
228             }
229              
230             sub fetch_row($$)
231             {
232             my($self, $data) = @_;
233              
234             if($self->{'cursor'} >= $data->{'rows'}) {
235             return;
236             }
237             $self->{'cursor'}++;
238              
239             my @fields = map { $self->{'data'}->{$self->{'cursor'}}->{$_ } } @{$self->{'col_names'}};
240             $self->{'row'} = \@fields;
241              
242             return $self->{'row'};
243             }
244              
245             sub seek($$$$)
246             {
247             my($self, $data, $pos, $whence) = @_;
248              
249             print "seek $pos $whence, not yet implemented\n";
250             }
251              
252             sub complete_table_name($$$$)
253             {
254             my($self, $meta, $file, $respect_case, $file_is_table) = @_;
255             }
256              
257             sub open_data
258             {
259             # my($className, $meta, $attrs, $flags) = @_;
260             }
261              
262             sub bootstrap_table_meta
263             {
264             my($class, $dbh, $meta, $table, @other) = @_;
265              
266             $class->SUPER::bootstrap_table_meta($dbh, $meta, $table, @other);
267              
268             $meta->{table} = $table;
269              
270             $meta->{sql_data_source} ||= __PACKAGE__;
271             }
272              
273             sub get_table_meta($$$$;$)
274             {
275             my($class, $dbh, $table, $file_is_table, $respect_case) = @_;
276              
277             my $meta = $class->SUPER::get_table_meta($dbh, $table, $respect_case, $file_is_table);
278              
279             $table = $meta->{table};
280              
281             return unless $table;
282              
283             return($table, $meta);
284             }
285              
286             =head1 AUTHOR
287              
288             Nigel Horne, C<< >>
289              
290             =head1 BUGS
291              
292             =head1 SEE ALSO
293              
294             L, which was also used as a template for this module.
295              
296             =head1 SUPPORT
297              
298             You can find documentation for this module with the perldoc command.
299              
300             perldoc DBD::XMLSimple
301              
302             You can also look for information at:
303              
304             =over 4
305              
306             =item * RT: CPAN's request tracker
307              
308             L
309              
310             =item * AnnoCPAN: Annotated CPAN documentation
311              
312             L
313              
314             =item * CPAN Ratings
315              
316             L
317              
318             =item * Search CPAN
319              
320             L
321              
322             =back
323              
324             =head1 LICENCE AND COPYRIGHT
325              
326             Copyright 2016-2017 Nigel Horne.
327              
328             This program is released under the following licence: GPL
329              
330             =cut
331              
332             1; # End of DBD::XMLSimple