Blame ex/perl_dbi_nulls_test.pl

Packit Service b125fd
#! /usr/bin/perl -w
Packit Service b125fd
Packit Service b125fd
# This script checks which style of WHERE clause(s) will support both
Packit Service b125fd
# null and non-null values.  Refer to the NULL Values sub-section
Packit Service b125fd
# of the "Placeholders and Bind Values" section in the DBI
Packit Service b125fd
# documention for more information on this issue.  The clause styles
Packit Service b125fd
# and their numbering (0-6) map directly to the examples in the
Packit Service b125fd
# documentation.
Packit Service b125fd
#
Packit Service b125fd
# To use this script:
Packit Service b125fd
#
Packit Service b125fd
# 1) If you are not using the DBI_DSN env variable, then update the
Packit Service b125fd
#    connect method arguments to support your database engine and
Packit Service b125fd
#    database, and remove the nearby check for DBI_DSN.
Packit Service b125fd
# 2) Set PrintError to 1 in the connect method if you want see the
Packit Service b125fd
#    engine's reason WHY your engine won't support a particular
Packit Service b125fd
#    style.
Packit Service b125fd
# 3) If your database does not support NULL columns by default
Packit Service b125fd
#    (e.g. Sybase) find and edit the CREATE TABLE statement
Packit Service b125fd
#    accordingly.
Packit Service b125fd
# 4) To properly test style #5, you need the capability to create the
Packit Service b125fd
#    stored procedure SP_ISNULL that acts as a function: it tests its
Packit Service b125fd
#    argument and returns 1 if it is null, 0 otherwise.  For example,
Packit Service b125fd
#    using Informix IDS engine, a definition would look like:
Packit Service b125fd
#
Packit Service b125fd
# CREATE PROCEDURE SP_ISNULL (arg VARCHAR(32)) RETURNING INTEGER;
Packit Service b125fd
#     IF arg IS NULL THEN RETURN 1; 
Packit Service b125fd
#     ELSE                RETURN 0;
Packit Service b125fd
#     END IF;
Packit Service b125fd
# END PROCEDURE;
Packit Service b125fd
#
Packit Service b125fd
# Warning: This script will attempt to create a table named by the
Packit Service b125fd
# $tablename variable (default dbi__null_test_tmp) and WILL DESTROY
Packit Service b125fd
# any pre-existing table so named.
Packit Service b125fd
Packit Service b125fd
use strict;
Packit Service b125fd
use DBI;
Packit Service b125fd
Packit Service b125fd
# The array represents the values that will be stored in the char column of our table.
Packit Service b125fd
# One array element per row.
Packit Service b125fd
# We expect the non-null test to return row 3 (Marge)
Packit Service b125fd
# and the null test to return rows 2 and 4 (the undefs).
Packit Service b125fd
		
Packit Service b125fd
my $homer = "Homer";
Packit Service b125fd
my $marge = "Marge";
Packit Service b125fd
Packit Service b125fd
my @char_column_values = (
Packit Service b125fd
  $homer,   # 1
Packit Service b125fd
  undef,    # 2
Packit Service b125fd
  $marge,   # 3
Packit Service b125fd
  undef,    # 4
Packit Service b125fd
);
Packit Service b125fd
Packit Service b125fd
# Define the SQL statements with the various WHERE clause styles we want to test
Packit Service b125fd
# and the parameters we'll substitute.
Packit Service b125fd
Packit Service b125fd
my @select_clauses =
Packit Service b125fd
(
Packit Service b125fd
  {clause=>qq{WHERE mycol = ?},                                         nonnull=>[$marge], null=>[undef]},
Packit Service b125fd
  {clause=>qq{WHERE NVL(mycol, '-') = NVL(?, '-')},                     nonnull=>[$marge], null=>[undef]},
Packit Service b125fd
  {clause=>qq{WHERE ISNULL(mycol, '-') = ISNULL(?, '-')},               nonnull=>[$marge], null=>[undef]},
Packit Service b125fd
  {clause=>qq{WHERE DECODE(mycol, ?, 1, 0) = 1},                        nonnull=>[$marge], null=>[undef]},
Packit Service b125fd
  {clause=>qq{WHERE mycol = ? OR (mycol IS NULL AND ? IS NULL)},        nonnull=>[$marge,$marge], null=>[undef,undef]},
Packit Service b125fd
  {clause=>qq{WHERE mycol = ? OR (mycol IS NULL AND SP_ISNULL(?) = 1)}, nonnull=>[$marge,$marge], null=>[undef,undef]},
Packit Service b125fd
  {clause=>qq{WHERE mycol = ? OR (mycol IS NULL AND ? = 1)},            nonnull=>[$marge,0],      null=>[undef,1]},
Packit Service b125fd
);
Packit Service b125fd
Packit Service b125fd
# This is the table we'll create and use for these tests.
Packit Service b125fd
# If it exists, we'll DESTROY it too.  So the name must be obscure.
Packit Service b125fd
Packit Service b125fd
my $tablename = "dbi__null_test_tmp"; 
Packit Service b125fd
Packit Service b125fd
# Remove this if you are not using the DBI_DSN env variable,
Packit Service b125fd
# and update the connect statement below.
Packit Service b125fd
Packit Service b125fd
die "DBI_DSN environment variable not defined"
Packit Service b125fd
	unless $ENV{DBI_DSN};
Packit Service b125fd
Packit Service b125fd
my $dbh = DBI->connect(undef, undef, undef,
Packit Service b125fd
  {
Packit Service b125fd
	  RaiseError => 0,
Packit Service b125fd
	  PrintError => 1
Packit Service b125fd
  }
Packit Service b125fd
) || die DBI->errstr;
Packit Service b125fd
Packit Service b125fd
printf "Using %s, db version: %s\n", $ENV{DBI_DSN} || "connect arguments", $dbh->get_info(18) || "(unknown)";
Packit Service b125fd
Packit Service b125fd
my $sth;
Packit Service b125fd
my @ok;
Packit Service b125fd
Packit Service b125fd
print "=> Drop table '$tablename', if it already exists...\n";
Packit Service b125fd
do { local $dbh->{PrintError}=0; $dbh->do("DROP TABLE $tablename"); };
Packit Service b125fd
Packit Service b125fd
print "=> Create table '$tablename'...\n";
Packit Service b125fd
$dbh->do("CREATE TABLE $tablename (myid int NOT NULL, mycol char(5))");
Packit Service b125fd
# Use this if your database does not support NULL columns by default:
Packit Service b125fd
#$dbh->do("CREATE TABLE $tablename (myid int NOT NULL, mycol char(5) NULL)");
Packit Service b125fd
Packit Service b125fd
print "=> Insert 4 rows into the table...\n";
Packit Service b125fd
Packit Service b125fd
$sth = $dbh->prepare("INSERT INTO $tablename (myid, mycol) VALUES (?,?)");
Packit Service b125fd
for my $i (0..$#char_column_values)
Packit Service b125fd
{
Packit Service b125fd
    my $val = $char_column_values[$i];
Packit Service b125fd
    printf "   Inserting values (%d, %s)\n", $i+1, $dbh->quote($val);
Packit Service b125fd
    $sth->execute($i+1, $val);
Packit Service b125fd
}
Packit Service b125fd
print "(Driver bug: statement handle should not be Active after an INSERT.)\n"
Packit Service b125fd
    if $sth->{Active};
Packit Service b125fd
Packit Service b125fd
# Run the tests...
Packit Service b125fd
Packit Service b125fd
for my $i (0..$#select_clauses)
Packit Service b125fd
{
Packit Service b125fd
    my $sel = $select_clauses[$i];
Packit Service b125fd
    print "\n=> Testing clause style $i: ".$sel->{clause}."...\n";
Packit Service b125fd
    
Packit Service b125fd
    $sth = $dbh->prepare("SELECT myid,mycol FROM $tablename ".$sel->{clause})
Packit Service b125fd
	or next;
Packit Service b125fd
Packit Service b125fd
    print "   Selecting row with $marge\n";
Packit Service b125fd
    $sth->execute(@{$sel->{nonnull}})
Packit Service b125fd
	or next;
Packit Service b125fd
    my $r1 = $sth->fetchall_arrayref();
Packit Service b125fd
    my $n1_rows = $sth->rows;
Packit Service b125fd
    my $n1 = @$r1;
Packit Service b125fd
    
Packit Service b125fd
    print "   Selecting rows with NULL\n";
Packit Service b125fd
    $sth->execute(@{$sel->{null}})
Packit Service b125fd
	or next;
Packit Service b125fd
    my $r2 = $sth->fetchall_arrayref();
Packit Service b125fd
    my $n2_rows = $sth->rows;
Packit Service b125fd
    my $n2 = @$r2;
Packit Service b125fd
    
Packit Service b125fd
    # Complain a bit...
Packit Service b125fd
    
Packit Service b125fd
    print "\n=>Your DBD driver doesn't support the 'rows' method very well.\n\n"
Packit Service b125fd
       unless ($n1_rows == $n1 && $n2_rows == $n2);
Packit Service b125fd
       
Packit Service b125fd
    # Did we get back the expected "n"umber of rows?
Packit Service b125fd
    # Did we get back the specific "r"ows we expected as identifed by the myid column?
Packit Service b125fd
    
Packit Service b125fd
    if (   $n1 == 1     # one row for Marge
Packit Service b125fd
        && $n2 == 2     # two rows for nulls
Packit Service b125fd
        && $r1->[0][0] == 3 # Marge is myid 3
Packit Service b125fd
        && $r2->[0][0] == 2 # NULL for myid 2
Packit Service b125fd
        && $r2->[1][0] == 4 # NULL for myid 4
Packit Service b125fd
    ) {
Packit Service b125fd
      print "=> WHERE clause style $i is supported.\n";
Packit Service b125fd
      push @ok, "\tStyle $i: ".$sel->{clause};
Packit Service b125fd
    }
Packit Service b125fd
    else
Packit Service b125fd
    {
Packit Service b125fd
      print "=> WHERE clause style $i returned incorrect results.\n";
Packit Service b125fd
      if ($n1 > 0 || $n2 > 0)
Packit Service b125fd
      {
Packit Service b125fd
        print "   Non-NULL test rows returned these row ids: ".
Packit Service b125fd
            join(", ", map { $r1->[$_][0] } (0..$#{$r1}))."\n";
Packit Service b125fd
        print "   The NULL test rows returned these row ids: ".
Packit Service b125fd
            join(", ", map { $r2->[$_][0] } (0..$#{$r2}))."\n";
Packit Service b125fd
      }
Packit Service b125fd
    }
Packit Service b125fd
}
Packit Service b125fd
Packit Service b125fd
$dbh->disconnect();
Packit Service b125fd
print "\n";
Packit Service b125fd
print "-" x 72, "\n";
Packit Service b125fd
printf "%d styles are supported:\n", scalar @ok;
Packit Service b125fd
print "$_\n" for @ok;
Packit Service b125fd
print "-" x 72, "\n";
Packit Service b125fd
print "\n";
Packit Service b125fd
print "If these results don't match what's in the 'Placeholders and Bind Values'\n";
Packit Service b125fd
print "section of the DBI documentation, or are for a database that not already\n";
Packit Service b125fd
print "listed, please email the results to dbi-users\@perl.org. Thank you.\n";
Packit Service b125fd
Packit Service b125fd
exit 0;