view release on metacpan or search on metacpan
t/TdTestBigInt.pm
t/TdTestBigSQL.pm
t/TdTestBulkload.pm
t/TdTestCursors.pm
t/TdTestDataGen.pm
t/TdTestProcs.pm
t/test.pl
t/testbigint.pl
t/testbigsql.pl
t/testcolbind.pl
t/testcursor.pl
t/testnbraw.pl
t/testsp.pl
- SSO
- LOB support
- UDF/XSP creation support
- mod_perl testing
- performance enhancements
- scrollable cursors
- more refactoring, esp. tdat_Unpack, io_fetch, Utility.pm
- prebuild common requests (commit, rollback, continue, etc.)
- better handling of BT/ET to track nested transactions
- add support for per-handle tracing via the TraceLevel attribute
V2R6.2 updates: VARDECIMAL, BIGINT support
doc/dbdtree.html view on Meta::CPAN
d.add(15, 6,'Connections, Sessions and Transactions', 'tdatdbd.html#cst', null, 'mainframe');
d.add(16, 6,'Connection Character Sets', 'tdatdbd.html#charsets', null, 'mainframe');
d.add(17, 6,'Data Types', 'tdatdbd.html#types', null, 'mainframe');
d.add(18, 6,'prepare() Optimization', 'tdatdbd.html#optimize', null, 'mainframe');
d.add(19, 6,'Parameterized SQL', 'tdatdbd.html#parmsql', null, 'mainframe');
d.add(20, 6,'Multi-Statement and MACRO Requests', 'tdatdbd.html#mstmts', null, 'mainframe');
d.add(21, 6,'Summarized SELECT Requests', 'tdatdbd.html#sumsel', null, 'mainframe');
d.add(22, 6,'Using execute_array()', 'tdatdbd.html#arybind', null, 'mainframe');
d.add(23, 6,'Utility Interfaces', 'tdatdbd.html#utilsupp', null, 'mainframe', 'img/closedbook.gif', 'img/openbook.gif');
d.add(24, 6,'Double Buffering', 'tdatdbd.html#dblbuf', null, 'mainframe');
d.add(25, 6,'Updatable Cursors', 'tdatdbd.html#cursors', null, 'mainframe');
d.add(26, 6,'Stored Procedures', 'tdatdbd.html#sprocs', null, 'mainframe');
d.add(27, 6,'Error Handling', 'tdatdbd.html#errors', null, 'mainframe');
d.add(28, 6,'Diagnostics', 'tdatdbd.html#diags', null, 'mainframe');
d.add(29, 6,'Driver-Specific Attributes', 'tdatdbd.html#specattr', null, 'mainframe', 'img/closedbook.gif', 'img/openbook.gif');
d.add(30, 6,'Driver-Specific Functions', 'tdatdbd.html#funcs', null, 'mainframe', 'img/closedbook.gif', 'img/openbook.gif');
d.add(31, 23,'ETL Utility Interface', 'tdatdbd.html#utilif', null, 'mainframe');
d.add(32, 23,'Fastload', 'tdatdbdfl.html', null, 'mainframe');
d.add(33, 23,'MultiLoad', 'tdatdbdml.html', null, 'mainframe');
d.add(34, 23,'FastExport', 'tdatdbdfx.html', null, 'mainframe');
doc/tdatdbd.html view on Meta::CPAN
Presicient recommends using the latest TTU 8.1 CLI libraries, regardless
of the version of Teradata server to which you will be connecting.<p>
CLI is *not* threadsafe on Unix platforms prior to TTU 8.0.<p>
There appears to be an issue with Linux and TTU 8.0 which causes
the CLI adapter to fail at initialization with an ICULOADERROR;
TTU 8.1 appears to function properly<p>
Rewindable cursors aren't supported, and updatable
cursors may have some issues with regard to updating the last
bufferful of a resultset. Both should be considered
unsupported at present.<p>
Double buffering is disabled in CLI mode, which may cause
some performance degradation for large result sets.<p>
The connection DSN must use a named server address, as CLI does not
support numeric IP address specification. Existing applications
which specified a fully qualified "COPn" hostname will be properly
modified by DBD::Teradata to trim the COPn suffix before passing the
doc/tdatdbd.html view on Meta::CPAN
"user",
"passwd,'\$H&Lmyaccount'")
or die "Cannot connect\n";
# more DBI calls...
</pre>
The following features are supported:<p>
<ul>
<li>Multiple open statements on a single connection
<li><a href="#cursors">Updatable cursors</a>
<li><a href="#sprocs">Stored procedures</a>
<li>transaction-spanning read-only cursors (via tdat_keepresp)
<li><a href="#dbccons">Remote Console</a> sessions
<li>Fastload
<li>EXPORT (<i>aka</i> Fastexport)
<li>MLOAD (<i>aka</i> Multiload)
<li>MONITOR (<i>aka</i> PM/API)
</ul>
In the event of unexpected session disconnection (e.g., network failure, or a session
that has been forced off the database), some platforms (notably UNIXen)
may receive a SIGPIPE signal. Currently, DBD::Teradata does not catch this signal,
doc/tdatdbd.html view on Meta::CPAN
<a name="dblbuf"></a>
<h3>Double Buffering</h3><p>
Double buffering (i.e., issuing a CONTINUE to the DBMS while the
application is still fetching data from the last received set of rowdata)
is supported, and is the default behavior. However, once a session executes
a SELECT...FOR CURSOR statement, double buffering is disabled for <b>all</b>
queries in the session. Double buffering is <i>not</i> supported for CLI adapter
connections.
<a name="cursors"></a>
<h3>Using Updatable Cursors</h3><p>
<ul>
<li>Cursor syntax is only supported in ANSI mode (i.e., <code>tdat_mode => 'ANSI'</code> during connect).<p>
<li>To open a cursor for positioned operations,
the <code><b>FOR CURSOR</b></code> clause must be appended to the cursor
<code>SELECT</code> statement.<p>
<li>Cursor names are generated internally; <code>DECLARE CURSOR</code> syntax is <b>not</b> supported.
The internally generated cursor name can be retrieved via the <b>CursorName</b> statement handle
attribute.<p>
<li>To apply an update or delete at the current position for a cursor,
the <code><b>WHERE CURRENT OF $sth->{CursorName}</b></code> clause must
be appended to the UPDATE or DELETE statement.<p>
<li>All the restrictions of updatable cursors described in the SQL Preprocessor manual
also apply. Especially note that <b>commit or rollback will implicitly close all
updatable cursors, and all read-only cursors not prepared with the <a href="#keepresp">tdat_keepresp</a> attribute
enabled!</b><p>
<li>The current row of the cursor will be invalidated whenever a <code>DELETE...WHERE CURRENT</code>
is successfully executed on the cursor.
The cursor must be explicitly advanced via any of the <code>fetch()</code> functions before continuing
with positioned operations.<p>
<li>Remember to turn off AutoCommit mode when using updatable cursors!<p>
<li>Note that using updatable cursors may adversely impact performance of other
non-cursor queries concurrently or subsequently opened on the same connection, since
supporting positioned updates precludes the ability to double-buffer responses
(as described above).<p>
<li>Due to an apparent anomoly in the PREPARE of positioned statements by the DBMS, positioned
statements must be prepare()'d either<p>
<ol>
<li>before any updatable cursor statement is execute()'d<p>
<li>after the associated cursor statement has been execute()'d <b>and</b> fetch()'d<p>
</ol>
</ul>
<p>
A simple example:
<pre>
$cursth = $dbh->prepare('SELECT * FROM mytable WHERE col1 > 12345 FOR CURSOR');
$updsth = $dbh->prepare("UPDATE mytable SET col3 = 'Invalid' WHERE CURRENT OF $cursth->{CursorName}");
$cursth->execute;
while ($cursth->fetch) {
doc/tdatdbd.html view on Meta::CPAN
<a name="keepresp"></a>
<h4>tdat_keepresp</h4>
<i>Write-only prepare() attribute.</i><br>
When set to a non-zero value, causes a KEEPRESP parcel to be issued with the request to the
DBMS. This useful for<p>
<ul>
<li>executing the EXPORT'd query on the control session of a fastexport (<b>Note</b> that the new
tdat_UtilitySetup() interface for EXPORT eliminates the need for the application to specify
this attribute).
<li>executing transaction-spanning read-only cursors (SELECT's without a FOR CURSOR suffix.).
</ul><p>
The latter case permits an application to execute a SELECT statement as a read-only cursor,
which remains open after subsequent commit or rollback operations, either via AutoCommit'ed
INSERT/UPDATE/DELETE/etc. statements, or by explicit commit() or rollback() calls. <b>NOTE</b>
that the application must <b>explicitly finish() the associated statement handle, even
after all rows have been retrieved from the cursor;</b> otherwise the cursor will remain open,
consuming both client and server resources, until the associated connection has been disconnected.
<p>
Example:
<pre>
my $selsth = $dbh->prepare('SELECT * from alltypetst', { tdat_keepresp => 1 });
my $updsth = $dbh->prepare('UPDATE alltypetst SET col2 = 1 WHERE col1 = ?');
my $row;
$selsth->execute;
while ($row = $selsth->fetchrow_arrayref) {
if ($$row[0]%100 == 0) {
doc/tdatdbd.html view on Meta::CPAN
</ul><p>
Release 2.1.0:<p>
<ul>
<li>improvments to PM API interface, including support for V2R4.1 PM API enhancements
<li>optimize prepare() of non-data returning requests
<li>minor fix for SQLSTATE mapping
</ul><p>
Release 2.0.4:<p>
<ul>
<li>fixed bug in tdat_UtilitySetup operation when no Checkpoint specified
<li>added support for transaction-spanning read-only cursors (ie, KEEPRESP)
<li>added SQLSTATE support (i.e., $drh/$dbh/$sth->state returns valid values)
<li>fixed non-conforming rowcount return value for $sth->execute and $dbh->do
</ul><p>
Release 2.0:<p>
<ul>
<li>first commercial release
</ul><p>
<a name="tips"></a>
<h2>Tips & Tricks</h2><p>
doc/tdatdbd.html view on Meta::CPAN
items; it is provided only to solicit feedback).
<ul>
<li>DBI metadata enhancements (<code>primary_keys()</code> metadata)
<li>Reconnection
<li>LOB support
<li>CREATE/REPLACE UDF/XSP support
<li>V2R6.2 VARDECIMAL/BIGINT datatypes
<li>quoted identifiers
<li>single sign on
<li>scrollable cursors
</ul>
<p>
<a name="refs"></a>
<h2>References</h2><p>
<ul>
<li><a target = '_blank' href="http://dbi.perl.org/">Official DBI Site</a>
<li><a target = '_blank' href="http://www.cpan.org">CPAN</a>
<li><a target = '_blank' href="http://www.perl.org">Perl.org</a>
lib/DBD/Teradata.pm view on Meta::CPAN
tdat_respsize => $attr->{tdat_respsize},
tdat_no_bigint => $attr->{tdat_no_bigint},
});
$dbh->{tdat_password} = $auth
if $attr->{tdat_lsn};
$iobj->[31] = $dbh;
$dbh->{tdat_uses_cli} = 1;
$dbh->{tdat_versnum} = $iobj->[40];
$dbh->{_iobj} = $iobj;
$dbh->{_stmts} = { };
$dbh->{_nextcursor} = 0;
$dbh->{_cursors} = { };
$dbh->{_debug} = $ENV{TDAT_DBD_DEBUG};
$dbh->{_utf8} = ($attr->{tdat_charset} eq 'UTF8');
$dbh->{Active} = 1;
$drh->{_connections}{($dsn . '_' . $dbh->{tdat_sessno})} = $dbh
unless $attr->{tdat_passthru};
return $outer;
}
sub data_sources {}
sub DESTROY {
$_[0]->disconnect_all();
lib/DBD/Teradata.pm view on Meta::CPAN
NULLABLE => [ 0 ],
tdat_TYPESTR => [ 'VARCHAR(255)' ],
tdat_TITLE => [ 'Compile Error' ],
tdat_FORMAT => [ 'X(255)' ],
_unpackstr => [ 'S/A' ],
NUM_OF_PARAMS => 0,
}
);
}
my $rowid = undef;
$rowid = $dbh->{_cursors}{uc $1}{_rowid}
if ($stmt=~/\s+WHERE\s+CURRENT\s+OF\s+([^\s;]+)\s*;?\s*$/i);
my $sth = $iobj->io_prepare($dbh, \&_make_sth, $stmt, $rowid, $attribs, $compatible, $passthru);
return $sth || $dbh->DBI::set_err($iobj->io_get_error());
}
sub _make_sth {
my ($dbh, $args) = @_;
delete $args->{tdat_clone};
delete $args->{tdat_passthru};
$args->{CursorName} = 'CURS' . $dbh->{_nextcursor};
$dbh->{_nextcursor}++;
$args->{tdat_stmt_num} = 0;
$args->{tdat_sessno} = $dbh->{tdat_sessno};
$args->{tdat_compatible} = '999.0'
unless $args->{tdat_compatible};
$args->{tdat_no_bigint} = $dbh->{tdat_no_bigint}
unless exists $args->{tdat_no_bigint};
$args->{ParamValues} = {}
unless $args->{ParamValues};
$args->{ParamArrays} = {}
unless $args->{ParamArrays};
lib/DBD/Teradata.pm view on Meta::CPAN
})
or return $dbh->set_err(-1, 'Unable to create statement handle.', 'S1000');
$sth->STORE('NUM_OF_PARAMS', delete $args->{NUM_OF_PARAMS});
$sth->STORE('NUM_OF_FIELDS', delete $args->{NUM_OF_FIELDS});
my ($key, $val);
$sth->{$key} = $val
while (($key, $val) = each %$args);
$dbh->{_stmts}{$sth->{CursorName}} = $sth;
Scalar::Util::weaken($dbh->{_stmts}{$sth->{CursorName}})
if $DBD::Teradata::HAS_WEAKEN;
$dbh->{_cursors}{$sth->{CursorName}} = $sth,
$sth->{tdat_keepresp} = 1,
$sthp[16] = 1
if ($#$stmtinfo == 1) &&
($stmtinfo->[1]{ActivityType} eq 'Select') &&
($sth->{Statement}=~/\s+FOR\s+CURSOR\s*;?\s*$/i);
$dbh->{tdat_nowait} = $sth->{tdat_nowait}
if ($dbh->{tdat_utility} ne 'DBC/SQL');
$sth->{tdat_TYPESTR} = DBD::Teradata::st::map_type2str($sth)
unless defined $sth->{tdat_TYPESTR} || (! $sth->{NUM_OF_FIELDS});
return wantarray ? ($outer, $sth) : $outer;
lib/DBD/Teradata.pm view on Meta::CPAN
defined($dbh->{Driver}));
my $host = $dbh->{Name} . '_' . $dbh->{tdat_sessno};
return 1
unless defined($dbh->{Driver}{_connections}{$host});
$dbh->disconnect;
}
sub disconnect {
my $dbh = shift;
my $i;
$dbh->{Active} = undef;
$dbh->{_stmts} = $dbh->{_cursors} = undef;
return 1
unless defined($dbh->{tdat_sessno}) &&
defined($dbh->{Name}) && defined($dbh->{Driver});
my $sessno = $dbh->{tdat_sessno};
my $host;
my $drh = $dbh->{Driver};
if ($sessno) {
$host = $dbh->{Name} . '_' . $sessno;
$dbh->set_err(0, 'Session not found'),
return 1
lib/DBD/Teradata.pm view on Meta::CPAN
if ($sth->{tdat_vartext_in} && ($#bind_values >= 0)) {
return undef
unless $sth->bind_param(1, $bind_values[0]);
$params = $sthp->[5];
}
my ($ptypes, $plens, $usephs) =
($sthp->[2], $sthp->[9], $sthp->[15]);
my ($sessno, $dbh, $partition) =
($sth->{tdat_sessno}, $sthp->[23], $iobj->[19]);
my $loading = (($partition == 5) || ($partition == 4));
my ($use_cursor, $cursnm, $cursth) = (0, '', undef);
if ($sth->{Statement}=~/\s+WHERE\s+CURRENT\s+OF\s+([^\s;]+)\s*;?$/i) {
$cursnm = uc $1;
$cursth = $dbh->{_cursors}{$cursnm};
return $sth->DBI::set_err(-1, 'Specified cursor not defined or not updatable.', 'S1000')
unless $cursth;
return $sth->DBI::set_err(-1, 'Specified cursor not positioned on a valid row.', 'S1000')
unless $cursth->{_p}[6];
$use_cursor = 1;
}
$iobj->[14] = 0,
$sth->{tdat_keepresp} = 1
if ($sth->{Statement}=~/\s+FOR\s+CURSOR\s*;?$/i);
my $rawmode = $sth->{tdat_raw_in};
my $modepcl =
($partition == 4) ? 104 :
(($partition == 6) ||
($rawmode && ($rawmode eq 'RecordMode'))) ? 3 :
68;
lib/DBD/Teradata.pm view on Meta::CPAN
}
$iobj->io_tddo('BT'),
$iobj->[11] = 1
if ($partition == 1) && (!$dbh->{AutoCommit}) &&
($dbh->{tdat_mode} ne 'ANSI') && ($iobj->[11] == 0);
$iobj->[11] = 1
if ($partition == 1) && ($dbh->{tdat_mode} eq 'ANSI');
return $tuples
if $attrs->{_fetch_sub};
my $rowcnt = $iobj->io_execute($sth, $datainfo, $indicdata,
($use_cursor ? $cursth->{_p}[6] : undef));
$sthp->[13] = $rowcnt;
return $sth->DBI::set_err($iobj->io_get_error())
unless defined($rowcnt);
$sth->{Active} = ($sth->{NUM_OF_FIELDS} != 0);
undef $sthp->[6]
if ($sth->{Statement}=~/^DELETE\s+.+\s+WHERE\s+CURRENT\s+OF\s+\w+$/i);
$sthp->[26] = 1
unless $sthp->[22] && (! $sth->{tdat_keepresp});
return ($rowcnt == 0) ? -1 : $rowcnt
if ($sth->{tdat_compatible} lt '2.0');
t/TdTestCursors.pm view on Meta::CPAN
package TdTestCursors;
use DBI qw(:sql_types);
use TdTestDataGen qw(collect_recs_each);
use Exporter;
use base ('Exporter');
@EXPORT = qw(init_for_cursors updatable_cursor persistent_cursor rewind_cursor);
use strict;
use warnings;
###################################################
#
# test updatable cursors
#
###################################################
sub init_for_cursors {
my ($dbh, $rowcnt) = @_;
$rowcnt ||= 1000;
$dbh->do('DELETE FROM alltypetst') or die $dbh->errstr;
print STDERR "Generating data...\n";
my $base = 0;
my $ary = collect_recs_each(\$base, $rowcnt, 0, 0);
print STDERR "Data generated, starting execution...\n";
my $ristarted = time;
t/TdTestCursors.pm view on Meta::CPAN
my $ok = 0;
$ok += $_
foreach (@tuple_status);
die "Unexpected tuplestatus values\n"
unless ($ok == scalar @tuple_status);
return $ristarted;
}
sub updatable_cursor {
my ($dbh, $dsn, $userid, $passwd) = @_;
print STDERR "Test updatable cursors...\n";
my $curdbh = DBI->connect("dbi:Teradata:$dsn", $userid, $passwd,
{
PrintError => 1,
RaiseError => 0,
AutoCommit => 0,
tdat_mode => 'ANSI',
tdat_charset => 'UTF8'
}
) || die "Can't connect to $dsn: $DBI::errstr. Exiting...\n";
t/TdTestCursors.pm view on Meta::CPAN
}
print STDERR "Processed $rowcnt rows\n";
$curdbh->commit or die $curdbh->errstr;
$curdbh->{AutoCommit} = 1;
$sth = $curdbh->prepare('select count(*) from alltypetst');
$sth->execute || die $sth->errstr;
$row = $sth->fetchrow_arrayref;
print "$$row[0] rows after test\n"
if ($row && defined($$row[0]));
$curdbh->disconnect;
print STDERR "Updatable cursors OK.\n";
}
###################################################
#
# test persistent read-only cursors
#
###################################################
sub persistent_cursor {
my $dbh = shift;
$dbh->{AutoCommit} = 0;
print STDERR "Testing persistent read-only cursors...\n";
my $sth = $dbh->prepare('SELECT * from alltypetst', { tdat_keepresp => 1 })
or die $dbh->errstr;
my $updsth = $dbh->prepare('UPDATE alltypetst SET col2 = 1 WHERE col1 = ?')
or die $dbh->errstr;
my ($row, $rowcnt);
for my $i (0..1) {
print 'Starting pass ', ($i+1), "\n";
$rowcnt = 0;
$sth->execute or die $sth->errstr;
while ($row = $sth->fetchrow_arrayref) {
t/TdTestCursors.pm view on Meta::CPAN
$dbh->commit;
print STDERR "applied update\n";
}
$rowcnt++;
last if ($rowcnt > 10000);
}
print STDERR "processed $rowcnt rows\n";
}
$sth->finish;
$dbh->{AutoCommit} = 1;
print STDERR "Persistent cursors OK.\n";
}
###################################################
#
# test cursor rewind
#
###################################################
sub rewind_cursor {
my $dbh = shift;
my $sth = $dbh->prepare('select count(*) from alltypetst');
$sth->execute || die $sth->errstr;
my $row = $sth->fetchrow_arrayref;
die "No rows for rewind test\n"
unless ($row && defined($$row[0]));
my $rewindrow = $$row[0] >> 1;
# $dbh->{AutoCommit} = 0;
print STDERR "Testing cursor rewind...\n";
$sth = $dbh->prepare('SELECT * from alltypetst', { tdat_keepresp => 1 })
or die $dbh->errstr;
my $rc = $sth->execute;
die $sth->errstr unless $rc;
print STDERR "Execute returned $rc\n";
my $rowcnt = 0;
while ($row = $sth->fetchrow_arrayref) {
$rowcnt++;
print STDERR "Rewinding...\n" and
$sth->tdat_Rewind()
use Config;
use TdTestDataGen qw(gen_test_data);
use TdTestBulkload qw(
load_nb_raw
load_nb_vartext
load_thrd_raw
load_thrd_vartext);
use TdTestBigInt qw(dectests);
use TdTestBigSQL qw(bigsqltest);
use TdTestCursors qw(
init_for_cursors
updatable_cursor
persistent_cursor
rewind_cursor);
*STDERR = *STDOUT;
my %typestr = (
SQL_VARCHAR, 'VARCHAR',
SQL_CHAR, 'CHAR',
SQL_FLOAT, 'FLOAT',
SQL_DECIMAL, 'DECIMAL',
SQL_INTEGER, 'INTEGER',
SQL_SMALLINT, 'SMALLINT',
$tristarted = load_thrd_raw($dsn, $userid, $passwd, $sescnt, 1000);
$trvstarted = load_thrd_vartext($dsn, $userid, $passwd, $sescnt, 1000);
}
else {
print STDERR "Perl built without thread support, skipping thread tests\n"
if $specials[10];
}
###################################################
#
# test updatable cursors
#
###################################################
if ($dbh->{tdat_uses_cli}) {
print STDERR "Using CLI, skipping persistent/rewindable cursors.\n";
}
else {
init_for_cursors($dbh, 1000);
updatable_cursor($dbh, $dsn, $userid, $passwd);
persistent_cursor($dbh);
rewind_cursor($dbh);
}
###################################################
#
# test output mode
#
###################################################
print STDERR "Testing standard output mode...\n";
init_for_cursors($dbh, 10000);
$ostarted = time;
$ssth = $dbh->prepare('SELECT * FROM alltypetst') or die ($dbh->errstr . "\n");
$names = $ssth->{NAME};
print join(' ', @$names), "\n";
$ssth->execute or die ($ssth->errstr . "\n");
$reccnt = 0;
while ($row = $ssth->fetchrow_arrayref() ) {
$reccnt++;
t/testcursor.pl view on Meta::CPAN
BEGIN {
push @INC, './t';
}
use DBI;
use DBD::Teradata;
use TdTestCursors qw(init_for_cursors updatable_cursor persistent_cursor rewind_cursor);
use strict;
use warnings;
$| = 1;
my $load;
while ($ARGV[0] && (substr($ARGV[0], 0, 1) eq '-')) {
my $op = shift @ARGV;
t/testcursor.pl view on Meta::CPAN
die "Did not connect with CLI adapter, check your configuration."
unless $dbh->{tdat_uses_cli} || $ENV{TDAT_DBD_NO_CLI};
print STDERR "Connected via ", ($dbh->{tdat_uses_cli} ? 'CLI' : 'pure Perl'), "\n";
#
# force dateform to integer
#
$dbh->do('set session dateform=integerdate');
init_for_cursors($dbh, 1000)
if $load;
updatable_cursor($dbh, $dsn, $userid, $passwd);
persistent_cursor($dbh);
rewind_cursor($dbh);
$dbh->disconnect;
print STDERR "Tests completed OK.\n";
sub cleanup {
my $dbh = shift;
$dbh->do( 'DROP TABLE alltypetst');
die $dbh->errstr