db-derby-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Army <qoz...@sbcglobal.net>
Subject Re: DERBY-308 just be done and .... (Re: [jira] Updated: (DERBY-308) Modify dblook to support "GENERATED BY DEFAULT AS IDENTITY")
Date Sat, 28 May 2005 01:03:09 GMT
TomohitoNakayama wrote:

> If possible , I want to work for DERBY-318 before DERBY-308 ....

Tomohito,

I hope you don't mind, but when I filed DERBY-318, I wasn't sure whether the 
problem was with the DERBY-167 patch or with the server.  So, in order to keep 
you from spending your time trying to track down a problem that wasn't your 
fault, I started looking at this issue from the server side of things--and I 
think I've found the problem.

To make a long story short, the NPE in the server happens because of the 
"toString()" method defined in DefaultNodeImpl.

Why?  Well, I try to explain it below.  Excuse the formality of it all, but I 
had to do it this way in order to keep it clear in my own head ;)

There are several pieces that make up the puzzle.  Let me start by saying what 
they are, and then I'll try to tie them together.

---------------
-- The pieces.
---------------

1) For a column that is GENERATED BY DEFAULT, the 'defaultText' variable in the 
corresponding DefaultNodeImpl is null.  Since DefaultNodeImpl.toString() just 
returns defaultText, the result of toString() on a DefaultNodeImpl that is 
GENERATED BY DEFAULT is null.

2) The underlying DataTypeDescriptor for all default columns is 
"org.apache.derby.iapi.types.UserType".  In that class, the "isNull()" method is 
simply written to return "(value == null)", while the "getString()" method is as 
follows:

public String getString()
{
	if (! isNull())
	{
		return value.toString();
	}
	else
	{
		return null;
	}
}

3) For a column that is GENERATED BY DEFAULT, the 'value' variable in UserType 
is a non-null instance of DefaultNodeImpl.

---------------
-- The problem.
---------------

In Network Server, we take the result set from the query "SELECT COLUMNDEFAULT 
FROM SYS.SYSCOLUMNS" and we do two things.  First, we call 
rs.getString(<columnNum>), and then we call rs.wasNull().

[ ** rs.getString() ** ]

* The call to rs.getString(...) on the "COLUMNDEFAULT" column ultimately makes a 
call to UserType.getString() (Piece #2).

* That method in turn calls UserType.isNull().

* UserType.isNull() looks at 'value', which is a non-null instance of 
DefaultNodeImpl(Piece #3 above), and since it's NOT null, it returns "false" 
(Piece #2).

* UserType.getString() looks at the result from UserType.isNull(), which is 
"false".  It then calls "value.toString()" (Piece #2).

* Since 'value' is an instance of DefaultNodeImpl (Piece #3), this ends up being 
a call to DefaultNodeImpl.toString().

* DefaultNodeImpl.toString() just returns 'defaultText', which is null for 
columns that are GENERATED BY DEFAULT (Piece #1).

* UserType.getString() returns the null value that it got from 
DefaultNodeImpl.toString().

* End result: the server call to "rs.getString()" returns null.

[ ** rs.wasNull() ** ]

* The server call to rs.wasNull() on the "COLUMNDEFAULT" column ultimately makes 
a call to UserType.isNull().

* UserType.isNull() looks at 'value', which is a non-null instance of 
DefaultNodeImpl(Piece #3 above), and since it's NOT null, it returns "false" 
(Piece #2).

* End result: the server call to "rs.wasNull()" returns FALSE.

THEREFORE, the server calls rs.getString() and gets NULL back, but then when it 
calls rs.wasNull(), it's told that the value it got back was NOT null.  So it 
tries to use the value (which IS null) and ends up with a Null Pointer Exception.

---------------
Possible fix:
---------------

Just to verify that this is in the fact the problem, I changed the "toString()" 
method in DefaultNodeImpl to the following:

public String	toString()
{
	if (isDefaultValueAutoinc())
		return "GENERATED BY DEFAULT";
	return defaultText;
}

What this means is that the server call to rs.getString() will now return 
"GENERATED BY DEFAULT" instead of null.  This in turn means that the result of 
rs.wasNull() will be correct, and the result of the query "SELECT COLUMNDEFAULT 
FROM SYS.SYSCOLUMNS" will be the string "GENERATED BY DEFAULT".

I then ran the repro described in DERBY-318, and it ran correctly, listing 
"GENERATED BY DEFAULT" as the default value for the column in question.

That said, I think returning any non-null value in DefaultNodeImpl.toString() 
will solve the problem  The question then becomes what value should we return? 
Is "GENERATED BY DEFAULT" an acceptable result?  To me, that seems like it 
should be okay--it says that the default value isn't known, but that it's 
generated at insertion time, if needed.  But that's just me--it's quite possible 
other people out there have a different opinion...

---------------
Conclusion:
---------------

1) This is NOT a Network Server problem (which is what I wanted to find out, and 
that's why I started looking at it).

2) This problem can be fixed with two lines :)  But I leave it up to the others 
on this list to decide if this is the _best_ fix, and to decide on what the 
non-null string value should be...

I hope that helps, and I hope you don't feel cheated because I took the time to 
look into this.  As I said, I wanted to make sure you weren't chasing a problem 
that had its origins in Network Server...

Army


Mime
View raw message