# DBI::Format - a package for displaying result tables
# Copyright (c) 1998 Jochen Wiedmann
# Copyright (c) 1998 Tim Bunce
# The DBI::Shell:Result module is free software; you can redistribute
# it and/or modify it under the same terms as Perl itself.
# Author: Jochen Wiedmann
$DBI::Format
::VERSION
= $DBI::Format
::VERSION
= substr(q
$Revision: 1.3 $, 10)+0;
sub available_formatters
{
my @dir = grep { -d
"$_/DBI/Format" } @INC;
opendir DIR
, "$dir/DBI/Format" or warn "Unable to read $dir/DBI: $!\n";
push @fmt, map { m/^(\w+)\.pm$/i ?
($1) : () } readdir DIR
;
my %fmt = map { (lc($_) => "DBI::Format::$_") } @fmt;
$fmt{box
} = "DBI::Format::Box";
$fmt{neat
} = "DBI::Format::Neat";
$fmt{raw
} = "DBI::Format::Raw";
$fmt{string
} = "DBI::Format::String";
$formatters = abbrev
(keys %fmt);
foreach my $abbrev (keys %$formatters) {
$formatters->{$abbrev} = $fmt{ $formatters->{$abbrev} } || die;
my ($class, $mode, $use_abbrev) = @_;
my $formatters = available_formatters
($use_abbrev);
my $fmt = $formatters->{$mode};
$formatters = available_formatters
(0);
die "Format '$mode' unavailable. Available formats: ".
join(", ", sort keys %$formatters)."\n";
unless (%{$class."::"}) {
package DBI
::Format
::Base
;
my $self = (@_ == 1) ?
{ %{shift()} } : { @_ };
bless ($self, (ref($class) || $class));
return $fh if ref($fh) =~ m/GLOB\(/;
if ($fh !~ /=/) { # not blessed
bless $fh => "FileHandle";
my $fh = delete $self->{'fh'};
my $sth = delete $self->{'sth'};
my $rows = delete $self->{'rows'};
print $fh ("[$rows rows of $sth->{NUM_OF_FIELDS} fields returned]\n");
package DBI
::Format
::Neat
;
@DBI::Format
::Neat
::ISA
= qw(DBI::Format::Base);
my($self, $sth, $fh, $sep) = @_;
$self->{'fh'} = $self->setup_fh($fh);
$self->{sep
} = $sep if defined $sep;
print $fh (join($self->{sep
}, @
{$sth->{'NAME'}}), "\n");
# XXX note that neat/neat_list output is *not* ``safe''
# in the sense the it does not escape any chars and
# may truncate the string and may translate non-printable chars.
# We only deal with simple escaping here.
print $fh (DBI
::neat_list
(\
@row, 9999, $self->{sep
}),"\n");
package DBI
::Format
::Box
;
@DBI::Format
::Box
::ISA
= qw(DBI::Format::Base);
my($self, $sth, $fh, $sep) = @_;
$self->{'fh'} = $self->setup_fh($fh);
$self->{sep
} = $sep if defined $sep;
my $types = $sth->{'TYPE'};
my $names = $sth->{'NAME'};
for (my $i = 0; $i < $sth->{'NUM_OF_FIELDS'}; $i++) {
push(@widths, defined($names->[$i]) ?
length($names->[$i]) : 0);
($type == DBI
::SQL_NUMERIC
() ||
$type == DBI
::SQL_DECIMAL
() ||
$type == DBI
::SQL_INTEGER
() ||
$type == DBI
::SQL_SMALLINT
() ||
$type == DBI
::SQL_FLOAT
() ||
$type == DBI
::SQL_REAL
() ||
$type == DBI
::SQL_BIGINT
() ||
$type == DBI
::SQL_TINYINT
()));
$self->{'widths'} = \
@widths;
$self->{'right_justify'} = \
@right_justify;
my($self, $orig_row) = @_;
my $widths = $self->{'widths'};
my @row = @
$orig_row; # don't mess with the original row
$_ =~ s/[\000-\037\177-\237]/./g;
if (length($_) > $widths->[$i]) {
$widths->[$i] = length($_);
push @
{$self->{data
}}, \
@row;
my $widths = delete $self->{'widths'};
my $right_justify = delete $self->{'right_justify'};
my $sth = $self->{'sth'};
my $data = $self->{'data'};
$self->{'rows'} = @
$data;
for (my $i = 0; $i < $sth->{'NUM_OF_FIELDS'}; $i++) {
$format_sep .= ('-' x
$widths->[$i]) . '+';
$format_names .= sprintf("%%-%ds|", $widths->[$i]);
$format_rows .= sprintf("%%"
. ($right_justify->[$i] ?
"" : "-") . "%ds|",
print $fh (sprintf($format_names, @
{$sth->{'NAME'}}));
foreach my $row (@
$data) {
print $fh (sprintf($format_rows, @
$row));
$self->SUPER::trailer
(@_);
package DBI
::Format
::Raw
;
@DBI::Format
::Raw
::ISA
= qw(DBI::Format::Base);
my($self, $sth, $fh, $sep) = @_;
$self->{'fh'} = $self->setup_fh($fh);
$self->{sep
} = $sep if defined $sep;
print $fh (join($self->{sep
}, @
{$sth->{'NAME'}}), "\n");
print $fh (join($self->{sep
}, @row), "\n");
package DBI
::Format
::String
;
@DBI::Format
::String
::ISA
= qw(DBI::Format::Base);
my($self, $sth, $fh, $sep) = @_;
$self->{'fh'} = $self->setup_fh($fh);
$self->{sep
} = $sep if defined $sep;
my $types = $sth->{'TYPE'};
my $names = $sth->{'NAME'};
for (my $i = 0; $i < $sth->{'NUM_OF_FIELDS'}; $i++) {
($type == DBI
::SQL_DATE
)?
8 :
($type == DBI
::SQL_INTEGER
and $sth->{PRECISION
}->[$i] > 15 )?
10 :
($type == DBI
::SQL_NUMERIC
and $sth->{PRECISION
}->[$i] > 15 )?
10 :
defined($sth->{PRECISION
}->[$i]) ?
$sth->{PRECISION
}->[$i]: 0);
($type == DBI
::SQL_NUMERIC
() ||
$type == DBI
::SQL_DECIMAL
() ||
$type == DBI
::SQL_INTEGER
() ||
$type == DBI
::SQL_SMALLINT
() ||
$type == DBI
::SQL_FLOAT
() ||
$type == DBI
::SQL_REAL
() ||
$type == DBI
::SQL_BIGINT
() ||
$type == DBI
::SQL_TINYINT
()));
$format_names .= sprintf("%%-%ds ", $widths[$i]);
print $fh (sprintf($format_names, $names->[$i]));
$self->{'widths'} = \
@widths;
$self->{'right_justify'} = \
@right_justify;
my($self, $orig_row) = @_;
my $widths = $self->{'widths'};
my $right_justify = $self->{'right_justify'};
my @row = @
$orig_row; # don't mess with the original row
$_ =~ s/[\000-\037\177-\237]/./g;
my $sth = $self->{'sth'};
my $data = $self->{'data'};
for (my $i = 0; $i < $sth->{'NUM_OF_FIELDS'}; $i++) {
$format_rows .= sprintf("%%"
. ($right_justify->[$i] ?
"" : "-") . "%ds ",
print $fh (sprintf($format_rows, @row));
my $widths = delete $self->{'widths'};
my $right_justify = delete $self->{'right_justify'};
$self->SUPER::trailer
(@_);
DBI::Format - A package for displaying result tables
# create a new result object
$r = DBI::Format->new('var1' => 'val1', ...);
# Prepare it for output by creating a header
# In a loop, display rows
while ($ref = $sth->fetchrow_arrayref()) {
# Finally create a trailer
THIS PACKAGE IS STILL VERY EXPERIMENTAL. THINGS WILL CHANGE.
This package is used for making the output of DBI::Shell configurable.
The idea is to derive a subclass for any kind of output table you might
a very simple output format as offered by DBI::neat_list().
L<"AVAILABLE SUBCLASSES">.
a box format, as offered by the Data::ShowTable module.
HTML format, as used in CGI binaries
postscript, to be piped into lpr or something similar
In the future the package should also support interactive methods, for
These are the available methods:
(Class method) This is the constructor. You'd rather call a subclass
constructor. The construcor is accepting either a list of key/value
(Instance method) This is called when a new result table should be
created to display the results of the statement handle B<$sth>. The
(optional) argument B<$fh> is an IO handle (or any object supporting
a I<print> method), usually you use an IO::Wrap object for STDIN.
The method will query the B<$sth> for its I<NAME>, I<NUM_OF_FIELDS>,
I<TYPE>, I<SCALE> and I<PRECISION> attributes and typically print a
header. In general you should not assume that B<$sth> is indeed a DBI
statement handle and better treat it as a hash ref with the above
(Instance method) Prints the contents of the array ref B<$ref>. Usually
you obtain this array ref by calling B<$sth-E<gt>fetchrow_arrayref()>.
(Instance method) Once you have passed all result rows to the result
package, you should call the I<trailer> method. This method can, for
example print the number of result rows.
=head1 AVAILABLE SUBCLASSES
First of all, you can use the DBI::Format package itself: It's
not an abstract base class, but a very simple default using
This subclass is using the I<Box> mode of the I<Data::ShowTable> module
internally. L<Data::ShowTable(3)>.
Row is written without formating. Columns returned in comma or user defined
Row is written using a string format. Future releases may include the ability
set the string format, if someone contributes it.
=head1 AUTHOR AND COPYRIGHT
This module is Copyright (c) 1997, 1998
The DBD::Proxy module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
L<DBI::Shell(3)>, L<DBI(3)>, L<dbish(1)>