Обсуждение: Issue with NULL varchars

Поиск
Список
Период
Сортировка

Issue with NULL varchars

От
"antony baxter"
Дата:
Hi,

I'm relatively knew to PostgreSQL, but have a fair amount of
experience with JDBC and Oracle/SQL Server.

I'm running PostgreSQL 8.3.0 on Mac OSX 10.5.2, am using the 8.3-603
JDBC drivers, and using Java 1.5.0_13.

I'm a bit confused by the results of the following test case code:

----

import java.sql.*;

public class jdbcLocaleTest
{
  public static void main(String args[])
  {
    try
    {
      Driver driver = (Driver)
Class.forName("org.postgresql.Driver").newInstance();
      DriverManager.registerDriver(driver);
      Connection c =
DriverManager.getConnection("jdbc:postgresql://localhost/testdb",
"testdb", "");

      int count, id = 1;
      PreparedStatement p;
      ResultSet rs;

      String language = "en";
      String country  = "GB";
      String variant  = null;

      // Create a test table:
      p = c.prepareStatement("CREATE TABLE testing (id int, language
varchar(2), country varchar(2), variant varchar(64))");
      p.execute();

      // Create a new Locale in the database:
      p = c.prepareStatement("INSERT INTO testing (id, language,
country, variant) VALUES (?, ?, ?, ?)");
      p.setInt(1, id);
      p.setString(2, language);
      p.setString(3, country);
      p.setNull(4, java.sql.Types.VARCHAR);  // or p.setString(4, variant);
      count = p.executeUpdate();
      System.out.println("INSERT: inserted " + count + " rows.");

      // Retrieve that Locale by its ID:
      p = c.prepareStatement("SELECT language, country, variant FROM
testing WHERE id = ?");
      p.setInt(1, id);
      rs = p.executeQuery();
      while (rs.next())
      {
        System.out.println("SELECT by id: language=" + rs.getString(1)
+ ", country=" + rs.getString(2) + ", variant=" + rs.getString(3));
      }
      rs.close();

      // Retrieve that Locale's ID by its Data:
      p = c.prepareStatement("SELECT id FROM testing WHERE language =
? AND country = ? AND variant = ?");
      p.setString(1, language);
      p.setString(2, country);
      p.setNull(3, java.sql.Types.VARCHAR);  // or p.setString(3, variant);
      rs = p.executeQuery();
      while (rs.next())
      {
        System.out.println("SELECT by data: Got id=" + rs.getInt(1));
      }
      rs.close();

      // Retrieve that Locale's ID by its Data:
      p = c.prepareStatement("SELECT COUNT(*) FROM testing WHERE variant = ?");
      p.setNull(1, java.sql.Types.VARCHAR);    // or p.setString(1, variant);
      rs = p.executeQuery();
      while (rs.next())
      {
        System.out.println("SELECT COUNT: count=" + rs.getInt(1));
      }
      rs.close();

      // Drop the test table:
      p = c.prepareStatement("DROP TABLE testing");
      p.execute();

      p.close();
    }
    catch (Exception e)
    {
      System.out.println("Error: " + e);
    }
  }
}

----

The output, when I run this, is

INSERT: inserted 1 rows.
SELECT by id: language=en, country=GB, variant=null
SELECT COUNT: count=0

which implies that the table is created, the row is inserted, the row
is retrieved when selecting by its Id, but when we try and search for
anything with a NULL varchar value, nothing is returned.

I've also tried swapping the setNull statements with setString(n,
null) - same result.

What am I missing?!

Many thanks,

Ant.

Re: Issue with NULL varchars

От
Craig Ringer
Дата:
antony baxter wrote:

You should probably enable query tracing, hand-execute the problem
queries, and examine what's going on that way.

However, if I'm not mistaken (which I could very easily be) your issue
is probably here:

>       p = c.prepareStatement("SELECT id FROM testing WHERE language =
> ? AND country = ? AND variant = ?");

If `variant' is NULL, this might be producing a query like:

SELECT id FROM testing WHERE language = 'en' AND country = 'GB' AND
variant = NULL ;

... which, because of the following rule for NULL equality:

craig=# SELECT (NULL = NULL) IS NULL;
 ?column?
----------
 t
(1 row)

won't match anything.

You probably wanted to say:

SELECT id FROM testing WHERE language 'en' AND country = 'GB' AND
variant IS NULL;


Yes, that's a pain.


It's possible to override this behaviour using a variable provided for
MS Access compatibility that treats 'NULL = NULL' as 't' instead of
NULL, but it might be better (more portable, safer, more
standards-compliant) to fix up the query.


> which implies that the table is created, the row is inserted, the row
> is retrieved when selecting by its Id, but when we try and search for
> anything with a NULL varchar value, nothing is returned.

If you:

SELECT * FROM testing;

you should see the inserted row, and if you execute the query your JDBC
layer generates from your parameterised query (which you can discover by
enabling query logging) it'll probably fail to match unless you rewrite
it to use IS NULL.

--
Craig Ringer

Re: Issue with NULL varchars

От
Craig Ringer
Дата:
antony baxter wrote:

>       // Retrieve that Locale's ID by its Data:
>       p = c.prepareStatement("SELECT COUNT(*) FROM testing WHERE variant = ?");

To avoid conditionally rewriting your query to use IS NULL / IS NOT NULL
instead of equality, you could also use IS DISTINCT FROM. Instead of:

    where variant = ?

try using:

    where not (variant is distinct from ?)


craig=# \pset null '<null>'
Null display is "<null>".

craig=# select null = null;
 ?column?
----------
 <null>
(1 row)

craig=# select not( null is distinct from null );
 ?column?
----------
 t
(1 row)

craig=# select not (1 is distinct from 1);
 ?column?
----------
 t
(1 row)

craig=# select not (1 is distinct from 2);
 ?column?
----------
 f
(1 row)

craig=# select not (null is distinct from 2);
 ?column?
----------
 f
(1 row)


See:

http://www.postgresql.org/docs/8.3/static/functions-comparison.html

--
Craig Ringer

Re: Issue with NULL varchars

От
"Albe Laurenz"
Дата:
antony baxter wrote:
> I'm relatively knew to PostgreSQL, but have a fair amount of
> experience with JDBC and Oracle/SQL Server.
>
> I'm running PostgreSQL 8.3.0 on Mac OSX 10.5.2, am using the 8.3-603
> JDBC drivers, and using Java 1.5.0_13.
>
> I'm a bit confused by the results of the following test case code:

That is surprising, because the behaviour is the same on Oracle.
I ran your program there.
Can't say about SQL Server.

[...]
>       // Retrieve that Locale's ID by its Data:
>       p = c.prepareStatement("SELECT COUNT(*) FROM testing WHERE variant = ?");
>       p.setNull(1, java.sql.Types.VARCHAR);    // or p.setString(1, variant);
>       rs = p.executeQuery();
>       while (rs.next())
>       {
>         System.out.println("SELECT COUNT: count=" + rs.getInt(1));
>       }
>       rs.close();
[...]

> The output, when I run this, is
[...]
> SELECT COUNT: count=0
>
> which implies that the table is created, the row is inserted, the row
> is retrieved when selecting by its Id, but when we try and search for
> anything with a NULL varchar value, nothing is returned.
>
> I've also tried swapping the setNull statements with setString(n, null) - same result.
>
> What am I missing?!

The fact that "NULL = NULL" evaluates to NULL and not to TRUE.

You can run the statement from the command line, without JDBC, and you will
get the same result.

Maybe you wanted to use:

SELECT COUNT(*) FROM testing WHERE variant IS NULL;

which would return a nonzero result.

Yours,
Laurenz Albe