Thread: Inserting into a uuid column

Inserting into a uuid column

From
Thomas Kellerer
Date:
Hi,

I have a table with a column of type UUID and I would like to import data into
that table.

I'm using a Java based import tool that will use a PreparedStatement and
setObject() (passing a String variable) to insert the data.

The problem is that the import does not work because Postgres throws an error:

"ERROR: column "guid" is of type uuid but expression is of type character varying"

What I don't understand is that doing an insert with a character literal:

INSERT INTO my_table (guid_column, ...)
VALUES
('a0eebc999c0b4ef8bb6d6bb9bd380a11', ...)

works without problems also when used from within Java.

But the following Java code will throw the above error:

PrparedStatement stmt = connection.prepareStatement(
     "insert into my_table (guid_column) values (?)");
stmt.setObject(1, "a0eebc999c0b4ef8bb6d6bb9bd380a11");
stmt.executeUpdate();


To me this sounds like a problem with the JDBC driver, or am I missing something?

I'm using PG 8.3.5 on Windows XP, the driver is postgresql-8.3-603.jdbc3.jar

Regards
Thomas

Re: Inserting into a uuid column

From
Kris Jurka
Date:

On Tue, 3 Mar 2009, Thomas Kellerer wrote:

> I have a table with a column of type UUID and I would like to import data
> into that table.
>
> I'm using a Java based import tool that will use a PreparedStatement and
> setObject() (passing a String variable) to insert the data.
>
> "ERROR: column "guid" is of type uuid but expression is of type character
> varying"

You should use setObject(<column>, <string value>, Types.OTHER) to
indicate that while you are passing a String, you aren't expecting the
server type to be a string datatype.

Alternatively, you can use the 8.4dev JDBC4 driver and issue:

setObject(<column>, <java.util.UUID object>);

> What I don't understand is that doing an insert with a character literal:
>
> INSERT INTO my_table (guid_column, ...)
> VALUES
> ('a0eebc999c0b4ef8bb6d6bb9bd380a11', ...)

Here you aren't providing any specific type information, just a literal.
By saying setString or setObject with a String parameter you are saying
that it really is a String.

Kris Jurka

Re: Inserting into a uuid column

From
Thomas Kellerer
Date:
Kris Jurka wrote on 03.03.2009 22:13:
> You should use setObject(<column>, <string value>, Types.OTHER) to
> indicate that while you are passing a String, you aren't expecting the
> server type to be a string datatype.

The problem is that this is a generic import tool, but I'll see what I can do.

>> INSERT INTO my_table (guid_column, ...)
>> VALUES
>> ('a0eebc999c0b4ef8bb6d6bb9bd380a11', ...)
>
> Here you aren't providing any specific type information, just a literal.
> By saying setString or setObject with a String parameter you are saying
> that it really is a String.

No I'm not using setString() in that example. The Java code would be:

Statement stmt = connection.createStatement();
stmt.executeUpdate("INSERT INTO my_table (guid_column) " +
" VALUES ('a0eebc999c0b4ef8bb6d6bb9bd380a11')");

So it's passing a literal and is not using a PreparedStatement

The Javadocs of setObject(int, Object) say:

"The given argument will be converted to the corresponding SQL type before being
sent to the database"

So I was expecting that the driver will be able to do the same conversion with
the PreparedStatement as it is obviously happening when using a literal (though
that conversion probably takes place on the server not in the driver).

Regards
Thomas

Re: Inserting into a uuid column

From
Oliver Jowett
Date:
Thomas Kellerer wrote:

> No I'm not using setString() in that example. The Java code would be:
>
> Statement stmt = connection.createStatement();
> stmt.executeUpdate("INSERT INTO my_table (guid_column) " +
> " VALUES ('a0eebc999c0b4ef8bb6d6bb9bd380a11')");
>
> So it's passing a literal and is not using a PreparedStatement

That should work identically to what you're doing via psql then, so
something strange is going on. Can you put together a testcase showing
the problem?

> The Javadocs of setObject(int, Object) say:
>
> "The given argument will be converted to the corresponding SQL type
> before being sent to the database"

The "corresponding SQL type" of a java.lang.String is Types.VARCHAR.

> So I was expecting that the driver will be able to do the same
> conversion with the PreparedStatement as it is obviously happening when
> using a literal (though that conversion probably takes place on the
> server not in the driver).

If you were using a PreparedStatement (which you're not anyway) then
it's more like executing 'PREPARE foo(varchar) AS INSERT INTO ....
VALUES ($1)' -- the driver does not substitute query values inline, but
uses the server's support for providing parameters and their types
out-of-line. There is some magic in the query parser that treats string
literals more as an unknown type to be inferred from the query context,
rather than a specific type. Specifying Types.OTHER to setObject() is
the way to tell the JDBC driver that the String you passed isn't
necessarily a text type and the same type inference should be done.

-O


Re: Inserting into a uuid column

From
Kris Jurka
Date:

On Tue, 3 Mar 2009, Thomas Kellerer wrote:

> Kris Jurka wrote on 03.03.2009 22:13:
>> You should use setObject(<column>, <string value>, Types.OTHER) to indicate
>> that while you are passing a String, you aren't expecting the server type
>> to be a string datatype.
>
> The problem is that this is a generic import tool, but I'll see what I can
> do.

The other option is to use the URL parameter stringtype=unspecified if you
cannot change the underlying code.

http://jdbc.postgresql.org/documentation/83/connect.html#connection-parameters

> So I was expecting that the driver will be able to do the same conversion
> with the PreparedStatement as it is obviously happening when using a literal
> (though that conversion probably takes place on the server not in the
> driver).

The reason the driver doesn't do this is twofold.

1) The driver doesn't know the target server type.  Retrieving that would
induce an extra network roundtrip for execution.

2) Sometimes the server doesn't know the the target type and it's better
for the driver/user to provide it.  Consider the call of an overloaded
function: SELECT myfunc(?), if it has an implementation for both float and
int, you want the user/driver to indicate which one should be called
because the server doesn't know.

Kris Jurka

Re: Inserting into a uuid column

From
Oliver Jowett
Date:
Oliver Jowett wrote:
> Thomas Kellerer wrote:
>
>> No I'm not using setString() in that example. The Java code would be:
>>
>> Statement stmt = connection.createStatement();
>> stmt.executeUpdate("INSERT INTO my_table (guid_column) " +
>> " VALUES ('a0eebc999c0b4ef8bb6d6bb9bd380a11')");
>>
>> So it's passing a literal and is not using a PreparedStatement
>
> That should work identically to what you're doing via psql then, so
> something strange is going on. Can you put together a testcase showing
> the problem?

Oh, sorry, I misread your email - this one works, so it's behaving as
expected :)

-O

Re: Inserting into a uuid column

From
Thomas Kellerer
Date:
Hi Kris,

thanks for your answers.

I can change the import to use setObject(int, Object, int) which works fine.

> The reason the driver doesn't do this is twofold.
>
> 1) The driver doesn't know the target server type.  Retrieving that
> would induce an extra network roundtrip for execution.
>
> 2) Sometimes the server doesn't know the the target type and it's better
> for the driver/user to provide it.  Consider the call of an overloaded
> function: SELECT myfunc(?), if it has an implementation for both float
> and int, you want the user/driver to indicate which one should be called
> because the server doesn't know.

I can see the problems there ;)

Regards
Thomas