[
https://issues.apache.org/jira/browse/DERBY-5407?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Mamta A. Satoor updated DERBY-5407:
-----------------------------------
Attachment: DERBY5407_patch1_diff.txt
Attaching a patch which has a fix for the issue and a new junit test fixture.
After debugging the issue, I have found that the serialization of object on the
wire from the network server end happens
correctly. The same serialized data is received by the client but when we try
to instantiate the TypeDescriptor based
on this serialized data, we get confused between "VARCHAR () FOR BIT DATA" and
"VARCHAR FOR BIT DATA".
Following is a brief explanation of the current logic and problem area.
Let's say, there is a database with following table
create table t2( a1 varchar( 30 ) for bit data, a2 varchar(25) );
When query below is run on top of the trunk in network server mode against the
databse above, we get following results
select columndatatype from sys.systables, sys.syscolumns where tablename='T2'
and tableid=referenceid;
COLUMNDATATYPE
---------------
VARCHAR () FOR&
VARCHAR(25)
Notice the length of the column "A1" is missing in syscolumns.COLUMNDATATYPE
syscolumns.columndatatype is a UDT. On the server side, the TypeDescriptor
associated with it gets initialized as shown below
case StoredFormatIds.VARBIT_TYPE_ID_IMPL:
schemaName = null;
unqualifiedName = TypeId.VARBIT_NAME;
JDBCTypeId = Types.VARBINARY;
break;
Note, that unqualifiedName has gotten "VARCHAR () FOR BIT DATA" assigned to it.
Later on in the server code, we associate
column width 30 with this TypeDescriptor for our specific example. So, the
TypeDescriptor has the correct information which will then be sent on the wire
to the client.
Following stack trace shows were we write this "VARCHAR () FOR BIT DATA" onto
the wire.
Thread [DRDAConnThread_2] (Suspended (breakpoint at line 323 in
BaseTypeIdImpl))
BaseTypeIdImpl.writeExternal(ObjectOutput) line: 323
ObjectOutputStream.writeExternalData(Externalizable) line: 1449
ObjectOutputStream.writeOrdinaryObject(Object, ObjectStreamClass,
boolean) line: 1418
ObjectOutputStream.writeObject0(Object, boolean) line: 1178
ObjectOutputStream.writeObject(Object) line: 347
TypeDescriptorImpl.writeExternal(ObjectOutput) line: 549
ObjectOutputStream.writeExternalData(Externalizable) line: 1449
ObjectOutputStream.writeOrdinaryObject(Object, ObjectStreamClass,
boolean) line: 1418
ObjectOutputStream.writeObject0(Object, boolean) line: 1178
ObjectOutputStream.writeObject(Object) line: 347
DDMWriter.writeUDT(Object, int) line: 1181
DRDAConnThread.writeFdocaVal(int, Object, int, int, int, boolean,
DRDAStatement, boolean) line: 8053
DRDAConnThread.writeFDODTA(DRDAStatement) line: 7236
DRDAConnThread.writeQRYDTA(DRDAStatement) line: 6970
DRDAConnThread.processCommands() line: 894
DRDAConnThread.run() line: 288
Subsequently, we also write the width 30 for the VARCHAR () FOR BIT DATA on the
wire for our example case.
Client goes through the deserialization of the UDT and during deserialization,
it finds VARCHAR () FOR BIT DATA and the width
30 for it. So far, client is reading what server has sent and things are in
sync. We use this information to construct a
TypeDescriptor. This happens through BaseTypeIdImpl.getTypeFormatId(). Here, we
look at the string representation of the type descriptor that we received on
the wire and choose the appropriate format id based on that string. The problem
is in this BaseTypeIdImpl.getTypeFormatId() code, because the code looks for
VARCHAR FOR BIT DATA rather than VARCHAR () FOR BIT DATA
(notice the missing parentheses) as shown below.
else if ( "VARCHAR FOR BIT DATA".equals( unqualifiedName ) ) {
return StoredFormatIds.VARBIT_TYPE_ID_IMPL; }
Since VARCHAR FOR BIT DATA and VARCHAR () FOR BIT DATA do not match, we do not
use format id VARBIT_TYPE_ID_IMPL
Later, we go through the following switch statement based on format id in
BaseTypeIdlImpl.toParsableString(TypeDescriptor)
switch (getTypeFormatId())
{
case StoredFormatIds.BIT_TYPE_ID_IMPL:
case StoredFormatIds.VARBIT_TYPE_ID_IMPL:
int rparen = retval.indexOf(')');
String lead = retval.substring(0, rparen);
retval = lead + td.getMaximumWidth() +
retval.substring(rparen);
break;
case StoredFormatIds.CHAR_TYPE_ID_IMPL:
case StoredFormatIds.VARCHAR_TYPE_ID_IMPL:
case StoredFormatIds.BLOB_TYPE_ID_IMPL:
case StoredFormatIds.CLOB_TYPE_ID_IMPL:
retval += "(" + td.getMaximumWidth() + ")";
break;
case StoredFormatIds.DECIMAL_TYPE_ID_IMPL:
retval += "(" + td.getPrecision() + "," + td.getScale() + ")";
break;
}
In the switch statement above, we are supposed to stuff in the width of the
varchar for bit data into the parentheses
ie VARCHAR () FOR BIT DATA should get converted into VARCHAR (30) FOR BIT DATA
for our specific example but we don't do it because of getTypeFormatd() code
above..
To solve this, I have found that if I change code in
BaseTypeIdImpl.getTypeFormatId() to look for VARCHAR () FOR BIT DATA, as
follows
else if ( "VARCHAR () FOR BIT DATA".equals( unqualifiedName ) ) {
return StoredFormatIds.VARBIT_TYPE_ID_IMPL; }
the problem gets fixed. I have not yet run derbyall and junit suite to see if
that change can cause any problems.
If there might be dependencies on the original "VARCHAR FOR BIT DATA" check,
then we can add additional check for
VARCHAR () FOR BIT DATA along with existing check for "VARCHAR FOR BIT DATA" as
shown below
else if ( "VARCHAR FOR BIT DATA".equals( unqualifiedName ) ) {
return StoredFormatIds.VARBIT_TYPE_ID_IMPL; }
else if ( "VARCHAR () FOR BIT DATA".equals( unqualifiedName ) ) {
return StoredFormatIds.VARBIT_TYPE_ID_IMPL; }
We will need to do similar thing for CHAR FOR BIT DATA. This will fix the jira
issue and it will also not break any dependencies
that might exist on "VARCHAR FOR BIT DATA" check.
The test that I have attached to the jira runs fine with my suggested changes.
Next, I will run derbyall and junit suite.
Please let me know if there is any feedback on the suggested fix.
> When run across the network, dblook produces unusable DDL for VARCHAR FOR BIT
> DATA columns.
> -------------------------------------------------------------------------------------------
>
> Key: DERBY-5407
> URL: https://issues.apache.org/jira/browse/DERBY-5407
> Project: Derby
> Issue Type: Bug
> Components: Tools
> Affects Versions: 10.8.1.2
> Reporter: Rick Hillegas
> Assignee: Mamta A. Satoor
> Labels: derby_triage10_9
> Attachments: DERBY5407_patch1_diff.txt, SystemCatalogTest.java
>
>
> In private correspondence, Mani Afschar Yazdi reports that dblook omits the
> length specification for VARCHAR FOR BIT DATA columns when run across the
> network. Embedded dblook runs fine. I can reproduce this problem as follows:
> 1) Bring up a server (here I am using port 8246).
> 2) Create a database with the following ij script:
> connect 'jdbc:derby://localhost:8246/memory:db;create=true';
> create table t( a varchar( 20 ) for bit data );
> 3) Now run dblook across the network:
> java -org.apache.derby.tools.dblook -d "jdbc:derby://localhost:8246/memory:db"
> This produces the following DDL for the table:
> CREATE TABLE "APP"."T" ("A" VARCHAR () FOR BIT DATA);
> A similar experiment using an embedded database produces usable DDL which
> includes a length specification for the VARCHAR FOR BIT DATA column.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators:
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira