Sponsoring The Perl Toolchain Summit 2025: Help make this important event another success Learn more

package TestLib;
use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK);
use Cwd;
@ISA = qw(Exporter);
@EXPORT_OK = qw(test_dir prove_reqs show_reqs connect default_recommended);
my $test_dsn = delete $ENV{DBI_DSN};
my $test_user = delete $ENV{DBI_USER};
my $test_pass = delete $ENV{DBI_PASS};
my $test_dir;
END { defined($test_dir) and rmtree $test_dir }
sub test_dir
{
unless ( defined($test_dir) )
{
$test_dir = File::Spec->rel2abs( File::Spec->curdir() );
$test_dir = File::Spec->catdir( $test_dir, "test_output_" . $$ );
$test_dir = VMS::Filespec::unixify($test_dir) if ( $^O eq 'VMS' );
rmtree $test_dir;
mkpath $test_dir;
}
return $test_dir;
}
sub check_mod
{
my ( $module, $version ) = @_;
my $mod_path = $module;
$mod_path =~ s|::|/|g;
$mod_path .= '.pm';
eval { require $mod_path };
$@ and return ( 0, $@ );
my $mod_ver = $module->VERSION();
$version = eval $version;
$mod_ver = eval $mod_ver;
$@ and return ( 0, $@ );
$version <= $mod_ver and return ( 1, $mod_ver );
return (
0,
sprintf(
"%s->VERSION() of %s doesn't satisfy requirement of %s",
$module, $mod_ver, $version
)
);
}
my %defaultRecommended = (
'DBI' => '1.616',
'DBD::File' => '0.40',
'DBD::CSV' => '0.30',
'DBD::DBM' => '0.06',
# 'DBD::AnyData' => '0.110',
);
sub default_recommended
{
return %defaultRecommended;
}
sub prove_reqs
{
my %requirements;
my %recommends;
{
my %req = ( 'SQL::Statement' => '1.32', );
my %missing;
while ( my ( $m, $v ) = each %req )
{
my ( $ok, $msg ) = check_mod( $m, $v );
$ok and $requirements{$m} = $msg;
$ok or $missing{$m} = $msg;
}
if (%missing)
{
my $missingMsg =
"YOU ARE MISSING REQUIRED MODULES: [ "
. join( ", ", keys %missing ) . " ]:\n"
. join( "\n", values(%missing) );
if ( $INC{'Test/More.pm'} )
{
Test::More::BAIL_OUT $missingMsg;
}
else
{
print STDERR "\n\n$missingMsg\n\n";
exit 0;
}
}
}
{
my %req =
$_[0]
? %{ $_[0] }
: %defaultRecommended;
while ( my ( $m, $v ) = each %req )
{
my ( $ok, $msg ) = check_mod( $m, $v );
## if ( !$ok and $INC{'Test/More.pm'} )
## {
## Test::More::diag($msg);
## }
$ok and $recommends{$m} = $msg;
}
}
return ( \%requirements, \%recommends );
}
sub show_reqs
{
my @proved_reqs = @_;
my $print;
if ( $INC{'Test/More.pm'} )
{
require File::Basename;
$print = (File::Basename::basename($0) =~ m/00/ ? Test::More->can("diag") : Test::More->can("note"));
}
else
{
$print = \*CORE::print;
}
&$print("# Using required:\n") if ( $proved_reqs[0] );
&$print( "# $_: " . $proved_reqs[0]->{$_} . "\n" ) for sort keys %{ $proved_reqs[0] };
&$print("# Using recommended:\n") if ( $proved_reqs[1] );
&$print( "# $_: " . $proved_reqs[1]->{$_} . "\n" ) for sort keys %{ $proved_reqs[1] };
return;
}
sub connect
{
my $type = shift;
defined($type)
and $type =~ m/^dbi:/i
and return TestLib::DBD->new( $type, @_ );
defined($type)
and $type =~ s/^dbd::/dbi:/i
and return TestLib::DBD->new( "$type:", @_ );
return TestLib::Direct->new(@_);
}
use Carp qw(croak);
use Params::Util qw(_ARRAY0 _ARRAY _HASH0 _HASH);
use Scalar::Util qw(blessed);
sub new
{
my ( $class, $flags ) = @_;
$flags ||= {};
my $parser = SQL::Parser->new( 'ANSI', $flags );
my %instance = (
parser => $parser,
cache => {},
);
my $self = bless( \%instance, $class );
return $self;
}
sub parser
{
return $_[0]->{parser};
}
sub command
{
my $self = $_[0];
return $self->{stmt}->command();
}
sub prepare
{
my ( $self, $sql, $attrs ) = @_;
my $stmt = SQL::Statement->new( $sql, $self->{parser} );
$self->{stmt} = $stmt;
$self->{stmt}->{errstr} or return $self;
return;
}
sub execute
{
my $self = shift;
my @params = @_; # bind params
my @args;
$args[0] =
defined( _HASH0( $params[0] ) ) && !blessed( $params[0] ) ? shift(@params) : $self->{cache};
$args[1] = \@params;
return $self->{stmt}->execute(@args);
}
sub do
{
my ( $self, $sql, $attrs, @args ) = @_;
return $self->prepare( $sql, $attrs )->execute(@args);
}
sub col_names
{
my $self = $_[0];
defined( $self->{stmt}->{NAME} )
and defined( _ARRAY( $self->{stmt}->{NAME} ) )
and return $self->{stmt}->{NAME};
my @col_names = map { $_->{name} || $_->{value} } @{ $self->{stmt}->{column_defs} };
return \@col_names;
}
sub all_cols
{
my $self = $_[0];
return $self->{stmt}->{all_cols};
}
sub tbl_names
{
my $self = $_[0];
my @tables = sort map { $_->name() } $self->{stmt}->tables();
return \@tables;
}
sub columns
{
my ( $self, @args ) = @_;
return $self->{stmt}->columns(@args);
}
sub tables
{
my ( $self, @args ) = @_;
return $self->{stmt}->tables(@args);
}
sub row_values
{
my ( $self, @args ) = @_;
return $self->{stmt}->row_values(@args);
}
sub where_hash
{
my $self = $_[0];
return $self->{stmt}->where_hash();
}
sub where
{
my $self = $_[0];
return $self->{stmt}->where();
}
sub params
{
my $self = $_[0];
return $self->{stmt}->params();
}
sub limit
{
my $self = $_[0];
return $self->{stmt}->limit();
}
sub offset
{
my $self = $_[0];
return $self->{stmt}->offset();
}
sub order
{
my ( $self, @args ) = @_;
return $self->{stmt}->order(@args);
}
sub selectrow_array
{
my $self = shift;
$self->do(@_);
my $result = $self->{stmt}->fetch_row();
return wantarray ? @$result : $result->[0];
}
sub fetch_row
{
my $self = $_[0];
return $self->{stmt}->fetch_row();
}
sub fetch_rows
{
my $self = $_[0];
my $rc = $self->{stmt}->fetch_rows();
return $rc;
}
# clone DBI function
sub fetchall_hashref
{
my ( $self, $key_field ) = @_;
my $i = 0;
my $names_hash = { map { $_ => $i++ } @{ $self->{stmt}->{NAME} } };
my @key_fields = ( ref $key_field ) ? @$key_field : ($key_field);
my @key_indexes;
my $num_of_fields = $self->{stmt}->{'NUM_OF_FIELDS'};
foreach (@key_fields)
{
my $index = $names_hash->{$_}; # perl index not column
$index = $_ - 1
if !defined $index && DBI::looks_like_number($_) && $_ >= 1 && $_ <= $num_of_fields;
croak("Field '$_' does not exist (not one of @{[keys %$names_hash]})")
unless defined $index;
push @key_indexes, $index;
}
my $rows = {};
my $all_rows = $self->{stmt}->fetch_rows();
my $NAME = $self->{stmt}->{NAME};
foreach my $row ( @{$all_rows} )
{
my $ref = $rows;
$ref = $ref->{ $row->[$_] } ||= {} for @key_indexes;
@{$ref}{@$NAME} = @$row;
}
return $rows;
}
sub rows
{
return $_[0]->{stmt}->{NUM_OF_ROWS};
}
sub errstr
{
defined( $_[0]->{stmt} ) and return $_[0]->{stmt}->errstr();
return $_[0]->{parser}->errstr();
}
sub finish
{
delete $_[0]->{stmt};
}
package TestLib::DBD;
sub new
{
my ( $class, $dsn, $attrs ) = @_;
$attrs ||= {};
my $dbh = DBI->connect( $dsn, undef, undef, $attrs );
my %instance = ( dbh => $dbh, );
my $self = bless( \%instance, $class );
return $self;
}
sub parser
{
return $_[0]->{dbh}->{sql_parser_object};
}
sub command
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->command();
}
sub prepare
{
my ( $self, $sql, $attr ) = @_;
my $sth = $self->{dbh}->prepare( $sql, $attr );
$self->{sth} = $sth and return $self;
return;
}
sub execute
{
my $self = shift;
return $self->{sth}->execute(@_);
}
sub do
{
my ( $self, $sql, $attrs, @args ) = @_;
return $self->prepare( $sql, $attrs )->execute(@args);
}
sub selectrow_array
{
my $self = shift;
$self->do(@_);
my $result = $self->{sth}->fetchrow_arrayref();
return wantarray ? @$result : $result->[0];
}
sub col_names
{
my $self = $_[0];
return $self->{sth}->{NAME};
}
sub all_cols
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->{all_cols};
}
sub tbl_names
{
my $self = $_[0];
my @tables = sort map { $_->name() } $self->{sth}->{sql_stmt}->tables();
return \@tables;
}
sub columns
{
my ( $self, @args ) = @_;
return $self->{sth}->{sql_stmt}->columns(@args);
}
sub tables
{
my ( $self, @args ) = @_;
return $self->{sth}->{sql_stmt}->tables(@args);
}
sub row_values
{
my ( $self, @args ) = @_;
return $self->{sth}->{sql_stmt}->row_values(@args);
}
sub where_hash
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->where_hash();
}
sub where
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->where();
}
sub params
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->params();
}
sub limit
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->limit();
}
sub offset
{
my $self = $_[0];
return $self->{sth}->{sql_stmt}->offset();
}
sub order
{
my ( $self, @args ) = @_;
return $self->{sth}->{sql_stmt}->order(@args);
}
sub fetch_row
{
my $self = $_[0];
return $self->{sth}->fetch();
}
sub fetch_rows
{
my $self = $_[0];
return $self->{sth}->fetchall_arrayref();
}
sub fetchall_hashref
{
my $self = shift;
return $self->{sth}->fetchall_hashref(@_);
}
sub rows
{
return $_[0]->{sth}->rows();
}
sub errstr
{
defined( $_[0]->{sth} ) and return $_[0]->{sth}->errstr();
return $_[0]->{dbh}->errstr();
}
sub finish
{
delete $_[0]->{sth};
}
1;