2

I've a strange situation with a little application in Java using a JDBC-OBDC. I'm inspecting a Database using the DatabaseMetaData Class. When I execute the program, everything works without anyproblem. But when I want to debug to see the values inside the Resulset containing the DatabaseMetaData a java.sql.SQLException is thrown only if I put a breakpoint within the while. Here's my code:

DatabaseMetaData patrol = con.getMetaData();
ResultSet answer = patrol.getTables(null, null, null, null);
        while(answer.next()) {
            if (answer.wasNull() == false) {
                tableNamesAsOne = tableNamesAsOne + answer.getString("TABLE_NAME") + " ";
            }
        }
        answer.close();

Why I cannot put my breakpoint in this section of code??

This is the printStackTrace.

Exception in thread "main" java.sql.SQLException: No data found
    at sun.jdbc.odbc.JdbcOdbc.standardError(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbc.SQLGetDataString(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbcResultSet.getDataString(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbcResultSet.getString(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbcResultSet.getString(Unknown Source)
    at Snooper.inspect(Snooper.java:56)
    at Snooper.<init>(Snooper.java:26)
    at Snooper.createAndShowGUI(Snooper.java:112)
    at Snooper.main(Snooper.java:125)

Line Snooper.java:56 in my code refers to

tableNamesAsOne = tableNamesAsOne + answer.getString("TABLE_NAME") + " ";

Thanks.

jomaora
  • 1,656
  • 3
  • 17
  • 26
  • 4
    Can you post the SQLException? Maybe it is some kind of timout, because you let the connection wait for too long time... – Christian Kuetbach Jan 25 '11 at 16:07
  • I've added the Exception in the post. Thanks. – jomaora Jan 25 '11 at 16:13
  • Your question isn't clear. What is the database (Oracle, MSSQL, MySQL)? What Java IDE are you using? Are you saying that when you run in the IDE without the breakpoint set it works, but when you set a breakpoint you get an exception? WHEN do you get the exception -- immediately, or the second time you pass through the `answer.next()`? – Jim Garrison Jan 25 '11 at 16:44
  • 1
    The DB is mapped into odbc, it's visible from the exception stacktrace. – bestsss Jan 25 '11 at 18:53
  • Well, I didn't specify any database because I'm using JDBC-OBDC and I switch from a Access DB from a Excel file and the error is the same. I explain me again. I'm working with Java 1.4 and I'm runnign this java file on Eclipse. Anyway, as I say, when I run the class everything works, but when I debug the code, it throws an exception only if I put my breakpoint within the while.. I think bestsss is right, we were talking with my teacher at class that it could be possible the different thread history, – jomaora Jan 25 '11 at 20:45

2 Answers2

3

I have installed SQL Server to reproduce your problem and verify it.

Short explanation

You must read the values ONLY ONCE and in the ORDER they appear in the SELECT. JdbcOdbc sucks. While debugging, you're reading them multiple times.

Long explanation

What you are doing is inspecting a stateful object in the debugger, which leads to dynamic results.

In this case it's the sun.jdbc.odbc.JdbcOdbcResultSet and executing the expression resultSet.getString(...) multiple times. The first time, it will work (in case your breakpoint suspends the Thread before the resultSet is asked). Then, the second time you (or your debugger) inspects the value of the expression again, the getString() method is called again and this method changes the internal state of the ResultSet object.

Although the method's name suggests it's a simple getter, it's not. It does more than that. It may actually retrieve the data from the database, change its internal position counters etc. You cannot execute the getter methods multiple times.

The ODBC Driver is a very bad thing and of low quality. Expect strange behavior and other dragons. You can get some debug information by enabling JdbcOdbc Tracing. That is done by setting a LogWriter on the DriverManager, before the JdbcOdbc-Bridge is activated:

java.sql.DriverManager.setLogWriter(new PrintWriter(System.out));

Then, you will get verbose debugging output of the JdbcOdbc-Driver like the following. It may help you to debug the problem you have. When debugging, just ensure to store the data retrieved from the ResultSet objects in local objects, so you can inspect them multiple times in the debugger.

DriverManager.getConnection("jdbc:odbc:testdbodbc")
JdbcOdbcDriver class loaded
registerDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.JdbcOdbcDriver@7b479feb]
DriverManager.initialize: jdbc.drivers = null
JDBC DriverManager initialized
    trying driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.JdbcOdbcDriver@7b479feb]
*Driver.connect (jdbc:odbc:testdbodbc)
JDBC to ODBC Bridge: Checking security
No SecurityManager present, assuming trusted application/applet
JDBC to ODBC Bridge 2.0001
Current Date/Time: Wed Jan 26 00:31:27 CET 2011
Loading JdbcOdbc library
Allocating Environment handle (SQLAllocEnv)
hEnv=115724512
Allocating Connection handle (SQLAllocConnect)
hDbc=116219184
Connecting (SQLDriverConnect), hDbc=116219184, szConnStrIn=DSN=testdbodbc
RETCODE = 1
WARNING - Generating SQLWarning...
SQLWarning: reason([Microsoft][ODBC SQL Server Driver][SQL Server]Changed database context to 'master'.) SQLState(01000) vendor code(5701)
SQLWarning: reason([Microsoft][ODBC SQL Server Driver][SQL Server]Changed language setting to us_english.) SQLState(01000) vendor code(5703)
*Connection.getMetaData
*DatabaseMetaData.getDriverName
Get connection info string (SQLGetInfo), hDbc=116219184, fInfoType=6, len=300
SQLSRV32.DLL
*DatabaseMetaData.getDriverVersion
Get connection info string (SQLGetInfo), hDbc=116219184, fInfoType=7, len=300
06.01.7600
*DatabaseMetaData.getDriverName
Get connection info string (SQLGetInfo), hDbc=116219184, fInfoType=6, len=300
SQLSRV32.DLL
Driver name:    JDBC-ODBC Bridge (SQLSRV32.DLL)
*DatabaseMetaData.getDriverVersion

P.S. And this was the reproduced exception, including line numbers of the Sun code for JDK 1.6.0_22. As you can see in the first line, this is what is printed out on the console when I inspected the getString() method.

Get string data (SQLGetData), hStmt=108067024, column=3, maxLen=257
RETCODE = 100
ERROR - No data found
java.sql.SQLException: No data found
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7138)
at sun.jdbc.odbc.JdbcOdbc.SQLGetDataString(JdbcOdbc.java:3907)
at sun.jdbc.odbc.JdbcOdbcResultSet.getDataString(JdbcOdbcResultSet.java:5698)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:354)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:411)
at sandbox.DatabaseMetadataTest.testDBMetadata(DatabaseMetadataTest.java:27)
mhaller
  • 14,122
  • 1
  • 42
  • 61
  • Ok... I see what you say about JDBC OBDC calling the data multiple times. Thanks... I have change the JDBC OBDC by a Driver for Access called link= DriverManager.getConnection("jdbc:jstels:mdb:AirLine.mdb"), and yes, it works without problem while debbuging. Actually, we are using JDBC ODBC because our teacher want us to test this way to connect to DB and explore the metaData. I know that it sucks. :P. Also thanks for the tip to see the log. Very interesting. – jomaora Jan 26 '11 at 00:08
  • so the driver was never fixed, now when you told that, I recall around 2000 we tried to use it for MSSQL and it was a disaster (had exactly the same issue, also could not randomly access columns, must be in order), so we switched to a 3rd party socket based one. Great find! – bestsss Jan 26 '11 at 23:04
1

Yeah, the debugger runs in a different thread than the metadata obtained by con.getMetaData();... so, you know, it's a different transaction with a different metadata.

Well, ok, that would be my 1st guess. I have not looked into Obdc driver source code to confirm.


Edit: thanks for mhaller excellent remark a made a 2nd look/guess: you call wasNull() prematurely, it has meaning after some get operation of the ResultSet. Copy from javadoc

 * Note that you must first call one of the getter methods
 * on a column to try to read its value and then call
 * the method <code>wasNull</code> to see if the value read was
 * SQL <code>NULL</code>.

phew, me sucks tonight!

bestsss
  • 11,796
  • 3
  • 53
  • 63
  • No, debugger runs in the same Thread, because it's using the same stack frame. – mhaller Jan 25 '11 at 21:29
  • You mean the stacktrace is printed by the debugger? – bestsss Jan 25 '11 at 21:45
  • no, i said stack frame, not stacktrace. two different things. Debugger evaluates expressions (e.g. for inspecting objects) in the same frame where the breakpoint is and that includes the specific thread. For example you can tell Eclipse to halt only certain threads or all of them. This would not be possible if debugger would use a separate thread. It's really the threads which are suspended. – mhaller Jan 25 '11 at 22:11
  • Ok, but if they run in the same Thread during debugging, why it can let me inspect what's inside the Resultset? Do I have to think that the metadata is protected or something like that? – jomaora Jan 25 '11 at 22:29
  • @mhaller that's an excellent point, i knew it was the same stack frame but I thought it was copied into a different thread (while the suspended thread is actually kept suspended for real). And that would mean a different set of threadLocals. Thanks! – bestsss Jan 25 '11 at 22:58
  • Ok... I add the specific line where the exception is thrown: where I called the answer.getString("TABLE_NAME"). I erase the if and I tested again and I hace the same problem. So it must not be the call of wasNull(). – jomaora Jan 25 '11 at 23:29