File Coverage

blib/lib/XML/Generator/DBI.pm
Criterion Covered Total %
statement 12 191 6.2
branch 0 108 0.0
condition 0 26 0.0
subroutine 4 18 22.2
pod 3 14 21.4
total 19 357 5.3


line stmt bran cond sub pod time code
1             # $Id: DBI.pm,v 1.8 2003/08/18 21:15:27 matt Exp $
2              
3             package XML::Generator::DBI;
4 10     10   176505 use strict;
  10         29  
  10         540  
5              
6 10     10   5710 use MIME::Base64;
  10         5884  
  10         801  
7 10     10   9275 use XML::SAX::Base;
  10         158084  
  10         405  
8              
9 10     10   126 use vars qw($VERSION @ISA);
  10         33  
  10         30459  
10              
11             $VERSION = '1.00';
12             @ISA = ('XML::SAX::Base');
13              
14             my %defaults = (
15             RootElement => "database",
16             QueryElement => "select",
17             RowElement => "row",
18             ColumnsElement => "columns",
19             ColumnElement => "column",
20             );
21              
22             sub new {
23 0     0 1   my $class = shift;
24 0           my $s = $class->SUPER::new(@_);
25 0           my $self = bless { %defaults, %$s }, ref($s);
26            
27 0           return $self;
28             }
29              
30             sub execute {
31 0     0 1   my $self = shift;
32 0           my ($query, $bind, %p) = @_;
33            
34 0           my %params = (%defaults, %$self, %p);
35            
36             # This might confuse people, but the methods are actually
37             # called on this proxy object, which is a mirror of $self
38             # with the new params inserted.
39 0           my $proxy = bless \%params, ref($self);
40            
41             # turn on throwing exceptions
42 0           local $proxy->{dbh}->{RaiseError} = 1;
43            
44 0           $proxy->pre_execute();
45 0           $proxy->execute_one($query, $bind);
46 0           $proxy->post_execute();
47            
48             }
49              
50             sub pre_execute {
51 0     0 0   my $proxy = shift;
52              
53             # TODO - figure out how to call set_document_locator() here
54            
55 0           $proxy->SUPER::start_document({});
56 0           $proxy->SUPER::start_prefix_mapping({ Prefix => 'dbi', NamespaceURI => 'http://axkit.org/NS/xml-generator-dbi' });
57 0           $proxy->send_start($proxy->{RootElement});
58             }
59              
60             sub post_execute {
61 0     0 0   my $proxy = shift;
62            
63 0           $proxy->send_end($proxy->{RootElement});
64 0           $proxy->SUPER::end_prefix_mapping({ Prefix => 'dbi', NamespaceURI => 'http://axkit.org/NS/xml-generator-dbi' });
65 0           $proxy->SUPER::end_document({});
66             }
67              
68             sub execute_one {
69 0     0 1   my ($self, $query, $bind, %p) = @_;
70              
71 0           my %params = (%defaults, %$self, %p);
72            
73             # create yet another proxy object
74 0           my $proxy = bless \%params, ref($self);
75            
76 0           my @bind;
77 0 0         if (defined($bind)) {
78 0 0         @bind = ref($bind) ? @{$bind} : ($bind);
  0            
79             }
80            
81 0           my $sth;
82 0 0         if (ref($query)) {
83             # assume its a statement handle
84 0           $sth = $query;
85 0           $query = "Unknown - executing statement handle";
86             }
87             else {
88 0           $sth = $proxy->{dbh}->prepare($query);
89             }
90            
91 0           $sth->execute(@bind);
92            
93             #open QueryElement if defined
94 0 0         if($proxy->{QueryElement}){
95 0 0         $proxy->send_start($proxy->{QueryElement}, $proxy->{ShowSQL} ? (query => $query) : ());
96             }
97            
98 0 0         my $names = $proxy->{LowerCase} ? $sth->{NAME_lc} : $sth->{NAME};
99            
100             #get index of group field
101 0           my $group_by_ind;
102 0 0         if(defined $proxy->{GroupBy}){
103 0           $group_by_ind = 0;
104 0           foreach my $name (@$names){
105 0 0         last if $name eq $proxy->{GroupBy};
106 0           $group_by_ind++;
107             }
108             }
109              
110             # output columns if necessary
111 0           $proxy->add_column_info($sth, $names);
112            
113 0           my @row;
114 0           $sth->bind_columns(
115 0           \( @row[ 0 .. $#{$names} ] )
116             );
117            
118 0           my $group;
119            
120 0           while ($sth->fetch) {
121 0           my @encoding;
122 0           my $i = 0;
123 0           foreach (@row) {
124 0 0 0       if (defined($_) and /[\x00-\x08\x0A-\x0C\x0E-\x19]/) {
125             # in foreach loops, $_ is an lvalue!
126 0           $_ = MIME::Base64::encode_base64($_);
127 0           $encoding[$i] = 'base64';
128             }
129 0           $i++;
130             }
131            
132 0 0         if ($proxy->{AsAttributes}) {
133 0           $proxy->send_attributes_row(\@row, $names, \@encoding, \$group, $group_by_ind);
134             }
135             else {
136 0           $proxy->send_tags_row(\@row, $names, \@encoding, \$group, $group_by_ind);
137             }
138             }
139              
140             # close previous group element if any
141 0 0         $proxy->send_end($proxy->{GroupElement}) if defined $proxy->{GroupElement};
142            
143             # close QueryElement if defined
144 0 0         $proxy->send_end($proxy->{QueryElement}) if $proxy->{QueryElement};
145             }
146              
147             sub send_tags_row {
148 0     0 0   my $proxy = shift;
149 0           my ($row, $names, $encoding, $group, $group_by_ind) = @_;
150            
151 0           $proxy->send_group($row, $group, $group_by_ind);
152            
153 0 0         $proxy->send_start($proxy->{RowElement}) if $proxy->{RowElement};
154            
155 0           my @stack;
156             my @el_stack;
157            
158             # for each column...
159 0           foreach my $i (0 .. $#{$names}) {
  0            
160             # skip group element
161 0 0 0       if (defined($proxy->{GroupBy}) and $i == $group_by_ind){
162 0           next;
163             }
164            
165             # get the element stack: address/street ->
166 0 0         if ($proxy->{ByColumnName}) {
167 0           @el_stack = split(/\//, $names->[$i]);
168 0 0         if(! defined $el_stack[0]){
169 0           shift @el_stack;
170             }
171             }
172             else {
173 0           @el_stack = ($names->[$i]);
174             }
175            
176 0           my $stack_len = $#stack;
177 0           my $el_stack_len = $#el_stack;
178            
179 0           my $ind = 0;
180 0   0       while ($el_stack[$ind] eq $stack[$ind] and
      0        
181             $ind <= $stack_len and
182             $ind <= $el_stack_len)
183             {
184 0           $ind ++;
185             }
186            
187 0 0 0       if ($el_stack[$ind] eq $stack[$ind] and $el_stack_len == $stack_len) {
188             # We're already at the end of the stack, so output the column
189 0 0         $proxy->send_tag($names->[$i], $row->[$i],
    0          
190             $encoding->[$i] ?
191             ('dbi:encoding' => $encoding->[$i])
192             :
193             ()
194             ) if defined($row->[$i]);
195             }
196             else {
197             # Otherwise we need to close all previous tags...
198 0           foreach my $n ($ind .. $stack_len){
199 0           $proxy->send_end(pop @stack);
200             }
201            
202             # And open all the new ones...
203 0           foreach my $n ($ind .. ($el_stack_len - 1) ){
204 0           push @stack, $el_stack[$n];
205 0           $proxy->send_start($el_stack[$n]);
206             }
207            
208             # Then send the column
209 0 0         $proxy->send_tag($el_stack[$el_stack_len], $row->[$i],
    0          
210             $encoding->[$i] ?
211             ('dbi:encoding' => $encoding->[$i])
212             :
213             ()
214             ) if defined($row->[$i]);
215             }
216             }
217            
218 0           $proxy->send_end(pop @stack) while(@stack);
219            
220 0 0         $proxy->send_end($proxy->{RowElement}) if $proxy->{RowElement};
221             }
222              
223             sub send_group {
224 0     0 0   my $proxy = shift;
225 0           my ($row, $group, $group_by_ind) = @_;
226            
227             # maintain GroupBy before RowElement
228 0 0         if (defined $proxy->{GroupBy}) {
229 0 0         if ($$group ne $row->[$group_by_ind]) { # a new group
230 0   0       my $group_element = $proxy->{GroupElement} || die "GroupElement not defined";
231            
232             # close previous group element if any
233 0 0         $proxy->send_end($group_element) if (defined $$group);
234            
235 0 0         if ($proxy->{GroupAttribute}) {
    0          
236             #send start and value as attribute
237 0           $proxy->send_start($group_element, $proxy->{GroupAttribute} => $row->[$group_by_ind]);
238             }
239             elsif ($proxy->{GroupValueElement}) {
240             #send start and value as element
241 0           $proxy->send_start($group_element);
242 0           $proxy->send_tag($proxy->{GroupValueElement}, $row->[$group_by_ind]);
243             }
244             else {
245 0           die "You have to define either 'GroupAttribute' or 'GroupValueElement'";
246             }
247 0           $$group = $row->[$group_by_ind];
248             }
249             }
250             }
251              
252             sub send_attributes_row {
253 0     0 0   my $proxy = shift;
254 0           my ($row, $names, $encoding, $group, $group_by_ind) = @_;
255              
256 0           my %attribs = map { $names->[$_] => $row->[$_] } # create hash
  0            
257 0           grep { defined $row->[$_] } # remove undef ones
258 0           grep { $names->[$_] ne $proxy->{GroupBy} } #remove group data
259 0           (0 .. $#{$names});
260            
261             # GroupElement
262 0           $proxy->send_group($row, $group, $group_by_ind);
263              
264 0           my $enc_cols = join(',',
265 0           map { $names->[$_] }
266 0           grep { $encoding->[$_] }
267 0           (0 .. $#{$names}));
268              
269 0           my $null_cols = join(',',
270 0           map { $names->[$_] }
271 0           grep { !defined $row->[$_] }
272 0           (0 .. $#{$names}));
273            
274 0 0         $proxy->send_tag($proxy->{RowElement}, undef, %attribs,
    0          
275             $null_cols ? ('dbi:null-columns' => $null_cols) : (),
276             $enc_cols ? ('dbi:encoded-columns' => $enc_cols) : (),
277             );
278             }
279              
280             sub add_column_info {
281 0     0 0   my $self = shift;
282 0           my ($sth, $names) = @_;
283            
284 0 0         return unless $self->{ShowColumns};
285 0 0         return unless $self->{dbh};
286            
287 0           my $types = $sth->{TYPE};
288 0           my $precision = $sth->{PRECISION};
289 0           my $scale = $sth->{SCALE};
290 0           my $null = $sth->{NULLABLE};
291 0           $self->send_start($self->{ColumnsElement});
292 0           foreach my $i (0 .. $#{$names}) {
  0            
293 0           my $type_info = $self->{dbh}->type_info($types->[$i]);
294 0 0         if ($self->{AsAttributes}) {
295 0           my %attribs;
296 0           $attribs{name} = $names->[$i];
297 0           $attribs{raw_type} = $types->[$i];
298 0 0         $attribs{type} = $type_info->{TYPE_NAME} if $type_info->{TYPE_NAME};
299 0 0         $attribs{size} = $type_info->{COLUMN_SIZE} if $type_info->{COLUMN_SIZE};
300 0 0         $attribs{precision} = $precision->[$i] if defined($precision->[$i]);
301 0 0         $attribs{scale} = $scale->[$i] if defined($scale->[$i]);
302 0 0         $attribs{nullable} = (!$null->[$i] ? "NOT NULL" : ($null->[$i] == 1) ? "NULL" : "UNKNOWN") if defined($null->[$i]);
    0          
    0          
303            
304 0           $self->send_tag($self->{ColumnElement}, undef, %attribs);
305             }
306             else {
307 0           $self->send_start($self->{ColumnElement});
308              
309 0           $self->send_tag(name => $names->[$i]);
310 0           $self->send_tag(raw_type => $types->[$i]);
311 0 0         $self->send_tag(type => $type_info->{TYPE_NAME}) if $type_info->{TYPE_NAME};
312 0 0         $self->send_tag(size => $type_info->{COLUMN_SIZE}) if $type_info->{COLUMN_SIZE};
313 0 0         $self->send_tag(precision => $precision->[$i]) if defined($precision->[$i]);
314 0 0         $self->send_tag(scale => $scale->[$i]) if defined($scale->[$i]);
315 0 0         $self->send_tag(nullable => (!$null->[$i] ? "NOT NULL" : ($null->[$i] == 1 ? "NULL" : "UNKNOWN"))) if defined($null->[$i]);
    0          
    0          
316              
317 0           $self->send_end($self->{ColumnElement});
318             }
319             }
320 0           $self->send_end($self->{ColumnsElement});
321             }
322              
323             # SAX utility functions
324              
325             sub sax1tosax2_attrs {
326 0     0 0   my $attrs = shift;
327 0           my %new_attrs;
328 0           foreach my $k (keys %$attrs) {
329 0 0         if ($k =~ /^dbi:(.*)$/) {
330 0           my $lname = $1;
331 0           $new_attrs{"{http://axkit.org/NS/xml-generator-dbi}$lname"} = {
332             Name => $k,
333             LocalName => $lname,
334             Prefix => 'dbi',
335             NamespaceURI => 'http://axkit.org/NS/xml-generator-dbi',
336             Value => $attrs->{$k},
337             };
338             }
339             else {
340 0           $new_attrs{"{}$k"} = {
341             Name => $k,
342             LocalName => $k,
343             Prefix => '',
344             NamespaceURI => '',
345             Value => $attrs->{$k},
346             };
347             }
348             }
349 0           return \%new_attrs;
350             }
351              
352             sub send_tag {
353 0     0 0   my $self = shift;
354 0           my ($name, $contents, %attributes) = @_;
355 0 0 0       $self->SUPER::characters({ Data => (" " x $self->{cur_indent}) }) if $self->{Indent} && $self->{cur_indent};
356 0           $self->SUPER::start_element({ Name => $name, Attributes => sax1tosax2_attrs(\%attributes) });
357 0           $self->SUPER::characters({ Data => $contents });
358 0           $self->SUPER::end_element({ Name => $name });
359 0 0         $self->new_line if $self->{Indent};
360             }
361              
362             sub send_start {
363 0     0 0   my $self = shift;
364 0           my ($name, %attributes) = @_;
365 0 0 0       $self->SUPER::characters({ Data => (" " x $self->{cur_indent}) }) if $self->{Indent} && $self->{cur_indent};
366 0           $self->SUPER::start_element({ Name => $name, Attributes => sax1tosax2_attrs(\%attributes) });
367 0           $self->{cur_indent}++;
368 0 0         $self->new_line if $self->{Indent};
369             }
370              
371             sub send_end {
372 0     0 0   my $self = shift;
373 0           my ($name) = @_;
374 0           $self->{cur_indent}--;
375 0 0 0       $self->SUPER::characters({ Data => (" " x $self->{cur_indent}) }) if $self->{Indent} && $self->{cur_indent};
376 0           $self->SUPER::end_element({ Name => $name });
377 0 0         $self->new_line if $self->{Indent};
378             }
379              
380             sub new_line {
381 0     0 0   my $self = shift;
382 0 0         $self->SUPER::characters({ Data => "\n" }) if $self->{cur_indent};
383             }
384              
385             1;
386             __END__