JDBC Large Object getBinaryStream returns -1 before EOF

From: "Ricky" <ricky(at)ekuma(dot)net>
To: <pgsql-bugs(at)postgresql(dot)org>
Cc: "Akira Kumakiri" <kumakiri(at)ekuma(dot)net>
Subject: JDBC Large Object getBinaryStream returns -1 before EOF
Date: 2001-08-18 09:36:48
Message-ID: 003301c127c9$4e22d560$de09713d@ekuma.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

Hi,

I'm making some code handling several kinds of files as large object. I found the
JDBC driver returned without finish reading the file. I made the test program
named lobtest.java, which is added at the end of this mail.

This test program read a file (default is "tomcat.gif" which is fetched from
Tomcat home page, but you can specify the name.), and write it into data base
named "lobtest". And then read it just after writing. But the size between the
original one and write/read one is different. Everytime, the file after
write/read is smaller.

Some one in Postgres Mailing List in Japan ( pgsql-jp(at)sra(dot)co(dot)jp ) found the same
problem and checked inside the JDBC driver source code. He thinks read() in
BlobInputStream.java has something wrong at the following part, then changed a
liitle bit, and worked fine. He says he does not know all the JDBC driver codes,
and is not clear whether this is the correct fix or not.

The following is the part of org/postgresql/largeobject/BlobInputStream.java

/**
* The minimum required to implement input stream
*/
public int read() throws java.io.IOException {
try {
if(buffer==null || bpos>=buffer.length) {
buffer=lo.read(bsize);
bpos=0;
}

// Handle EOF
if(bpos>=buffer.length)
return -1;

int tmp = (int)buffer[bpos++]; //**** originally return
(int)buffer[bpos++];
if (tmp<0) tmp = tmp+256; //**** but, replaced using tmp as left side
shows.
return tmp; //**** and worked fine.
} catch(SQLException se) {
throw new IOException(se.toString());
}
}

You can reproduce this by the follwoing steps.

1) > createdb lobtest

2) >psql lobtest
lobtest=# CREATE TABLE tests (
index serial,
imgname text,
imgoid oid
);

3) copy the following test program into file (lobtest.java), then compile it.

4) type >java lobtest -i foo.gif
or whatever file you have on the same directory as lobtest.class file.
From my experience, all the gif file causes an error.
> java lobtest ? shows help.

5) lobtest reads file, stores it onto db as large object, then reads it again, and
writes it on the same directory. If the file is tomcat.gif, then r_tomcat.gif is
created. Please check the file size on both.

6) If I use getBytes("imgoid"), then no error.

Thank you for reading my poor English.

akira (ricky(at)ekuma(dot)net or kumakiri(at)ekuma(dot)net )

/*
* lobtest.java
*
*/

import java.io.*;
import java.lang.*;
import java.sql.*;
import java.util.*;
import sun.io.*;

class lobtest {
static String index = null;
static String filename = "tomcat.gif";
static String url = "jdbc:postgresql:lobtest";
static String account = "postgres";
static String password = "postgrespassword";
public static void main(String argv[]) {
for (int optind = 0; optind < argv.length; optind++) {
if (argv[optind].equals("-i")) {
filename = argv[++optind];
} else if (argv[optind].equals("-s")) {
System.out.println("System default encoding = "
+System.getProperty("file.encoding"));
System.out.println("ByteToCharConverter= "
+ByteToCharConverter.getDefault());
System.out.println("CharToByteConverter= "
+CharToByteConverter.getDefault());
System.exit(0);
} else if (argv[optind].equals("--")) {
optind++;
break;
} else if (argv[optind].startsWith("?")) {
System.out.println("Usage: test [-i file] [-s] [?]");
System.out.println(" (no param), write large object on db, and read
it, then compare it.");
System.out.println(" -i file, specify file name, default is
tomcat.gif");
System.out.println(" -s show system parameters");
System.out.println(" ? show this help");
System.exit(0);
} else break;
}

try { // establish DB connections.
Class.forName("org.postgresql.Driver");
} catch(ClassNotFoundException cnf) {
cnf.printStackTrace();
System.out.println("Class.forName() error.");
System.exit(1);
}

// Starting...
// Write file into DB as Large Object.
String query = null;
int length = 0;
Connection connM = null;
byte[] byteArray = null;
try {
File file = new File(filename);
FileInputStream fis = new FileInputStream(file);
int c = fis.available();
byteArray = new byte[c];
int get = fis.read(byteArray);
fis.close();
length = byteArray.length;
System.out.println("c= " +c +" get= " +get +" byteArray.length= " +length);

connM = DriverManager.getConnection(url, account, password);
connM.setAutoCommit(false);
InputStream bis = new ByteArrayInputStream(byteArray);
query = "INSERT INTO tests (imgname, imgoid) VALUES ('" +filename +"', ?);";
PreparedStatement ps = connM.prepareStatement(query);
ps.setBinaryStream(1, bis, length);
int result = ps.executeUpdate();
connM.commit();
ps.close();
bis.close();
System.out.println("filename= " +filename +" file.length= " +length +"
result= " +result);
} catch(SQLException sql) {
sql.printStackTrace();
} catch(FileNotFoundException fnf) {
fnf.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
} finally {
try {
connM.setAutoCommit(true);
} catch (Exception againse) {
System.out.print("write Exception again at setAutoCommit(true)");
System.exit(1);
}
}

// The follwoing is the way to read large object using InputStream, but
unexpected -1 returns.
// BlobInputStream.java causes the error ???.
try {
connM.setAutoCommit(false);
query = "SELECT imgoid FROM tests WHERE imgname='" +filename +"';";
Statement st = connM.createStatement();
ResultSet rs = st.executeQuery(query);

// ****** Case 1 ******
while(rs.next()) {
InputStream is = rs.getBinaryStream("imgoid");
BufferedInputStream bis = new BufferedInputStream(is);
String pathname = "./r_" +filename;
File file = new File(pathname);
boolean del = file.delete(); // test this again and again, so delete old
one.
if ( ! file.canRead()) { // always false. debugging now.
FileOutputStream fos = new FileOutputStream(file);
int c; // stream may return 0 when the method available() is called.
int i = 0;
while ((c=bis.read()) != -1) {fos.write(c); ++i;}// don't use
available() see java.sql
fos.close();
System.out.println("total read bytes i= " +i);
bis.close();
}
}

// ***** Case 2 *****
// You can avoid the above error by using getBytes() instead.
/* while(rs.next()) {
byte[] stb = rs.getBytes("imgoid");
String pathname = "./r_" +filename;
File file = new File(pathname);
boolean del = file.delete(); // test this again and again, so delete old
one.
if ( ! file.canRead()) { // always false. debugging now.
FileOutputStream fos = new FileOutputStream(file);
fos.write(stb);
fos.close();
System.out.println("total read bytes stb.length= " +stb.length);
}
} */

connM.commit();
rs.close();
st.close();
} catch(SQLException sql) {
sql.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
} finally {
try {
connM.setAutoCommit(true);
} catch (Exception againse) {
System.out.print("read Exception again at setAutoCommit(true)");
System.exit(1);
}
}

}

} // class end.

Browse pgsql-bugs by date

  From Date Subject
Next Message Akira Kumakiri 2001-08-18 09:55:03 JDBC Large Object getBinaryStream returns -1 before EOF
Previous Message Pedro Alves 2001-08-17 08:46:16 Bug Report