diff --git a/pgjdbc/src/main/java/org/postgresql/largeobject/BlobInputStream.java b/pgjdbc/src/main/java/org/postgresql/largeobject/BlobInputStream.java index eafa2ad15c..1dd25366ed 100644 --- a/pgjdbc/src/main/java/org/postgresql/largeobject/BlobInputStream.java +++ b/pgjdbc/src/main/java/org/postgresql/largeobject/BlobInputStream.java @@ -23,7 +23,7 @@ public class BlobInputStream extends InputStream { /** * The absolute position. */ - private long apos; + private long absolutePosition; /** * Buffer used to improve performance. @@ -33,17 +33,17 @@ public class BlobInputStream extends InputStream { /** * Position within buffer. */ - private int bpos; + private int bufferPosition; /** * The buffer size. */ - private int bsize; + private final int bufferSize; /** * The mark position. */ - private long mpos = 0; + private long markPosition = 0; /** * The limit. @@ -74,9 +74,9 @@ public BlobInputStream(LargeObject lo, int bsize) { public BlobInputStream(LargeObject lo, int bsize, long limit) { this.lo = lo; buffer = null; - bpos = 0; - apos = 0; - this.bsize = bsize; + bufferPosition = 0; + absolutePosition = 0; + this.bufferSize = bsize; this.limit = limit; } @@ -86,26 +86,24 @@ public BlobInputStream(LargeObject lo, int bsize, long limit) { public int read() throws java.io.IOException { LargeObject lo = getLo(); try { - if (limit > 0 && apos >= limit) { + if (limit > 0 && absolutePosition >= limit) { return -1; } - if (buffer == null || bpos >= buffer.length) { - buffer = lo.read(bsize); - bpos = 0; + // read more in if necessary + if (buffer == null || bufferPosition >= buffer.length) { + buffer = lo.read(bufferSize); + bufferPosition = 0; } // Handle EOF - if (buffer == null || bpos >= buffer.length) { + if ( buffer == null || bufferPosition >= buffer.length) { return -1; } - int ret = (buffer[bpos] & 0x7F); - if ((buffer[bpos] & 0x80) == 0x80) { - ret |= 0x80; - } + int ret = (buffer[bufferPosition] & 0xFF); - bpos++; - apos++; + bufferPosition++; + absolutePosition++; return ret; } catch (SQLException se) { @@ -113,6 +111,63 @@ public int read() throws java.io.IOException { } } + @Override + public int read(byte[] b, int off, int len) throws IOException { + int bytesCopied = 0; + LargeObject lo = getLo(); + + /* check to make sure we aren't at the limit + * funny to test for 0, but I guess someone could create a blob + * with a limit of zero + */ + if ( limit >= 0 && absolutePosition >= limit ) { + return -1; + } + + /* check to make sure we are not going to read past the limit */ + if ( limit >= 0 && len > limit - absolutePosition ) { + len = (int)(limit - absolutePosition); + } + + try { + // have we read anything into the buffer + if ( buffer != null ) { + // now figure out how much data is in the buffer + int bytesInBuffer = buffer.length - bufferPosition; + // figure out how many bytes the user wants + int bytesToCopy = len > bytesInBuffer ? bytesInBuffer : len; + // copy them in + System.arraycopy(buffer, bufferPosition, b, off, bytesToCopy); + // move the buffer position + bufferPosition += bytesToCopy; + // position in the blob + absolutePosition += bytesToCopy; + // increment offset + off += bytesToCopy; + // decrement the length + len -= bytesToCopy; + bytesCopied = bytesToCopy; + } + + if (len > 0 ) { + bytesCopied += lo.read(b, off, len); + buffer = null; + bufferPosition = 0; + absolutePosition += bytesCopied; + /* + if there is a limit on the size of the blob then we could have read to the limit + so bytesCopied will be non-zero but we will have read nothing + */ + if ( bytesCopied == 0 && (buffer == null) ) { + return -1; + } + } + } catch (SQLException ex ) { + throw new IOException(ex.getCause()); + } + return bytesCopied; + } + /** *
Closes this input stream and releases any system resources associated with the stream.
* @@ -153,7 +208,7 @@ public void close() throws IOException { * @see java.io.InputStream#reset() */ public synchronized void mark(int readlimit) { - mpos = apos; + markPosition = absolutePosition; } /** @@ -166,13 +221,13 @@ public synchronized void mark(int readlimit) { public synchronized void reset() throws IOException { LargeObject lo = getLo(); try { - if (mpos <= Integer.MAX_VALUE) { - lo.seek((int)mpos); + if (markPosition <= Integer.MAX_VALUE) { + lo.seek((int)markPosition); } else { - lo.seek64(mpos, LargeObject.SEEK_SET); + lo.seek64(markPosition, LargeObject.SEEK_SET); } buffer = null; - apos = mpos; + absolutePosition = markPosition; } catch (SQLException se) { throw new IOException(se.toString()); } diff --git a/pgjdbc/src/main/java/org/postgresql/largeobject/LargeObject.java b/pgjdbc/src/main/java/org/postgresql/largeobject/LargeObject.java index 184f4c81ed..ca5a930d99 100644 --- a/pgjdbc/src/main/java/org/postgresql/largeobject/LargeObject.java +++ b/pgjdbc/src/main/java/org/postgresql/largeobject/LargeObject.java @@ -398,6 +398,20 @@ public InputStream getInputStream(long limit) throws SQLException { return new BlobInputStream(this, 4096, limit); } + /** + * Returns an {@link InputStream} from this object, that will limit the amount of data that is + * visible. + * Added mostly for testing + * + * @param maxSize internal buffer size + * @param limit maximum number of bytes the resulting stream will serve + * @return {@link InputStream} from this object + * @throws SQLException if a database-access error occurs. + */ + public InputStream getInputStream(int maxSize, long limit) throws SQLException { + return new BlobInputStream(this, maxSize, limit); + } + /** *Returns an {@link OutputStream} to this object.
* diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BlobTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BlobTest.java index 3ce07a9fdc..557ad58775 100644 --- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BlobTest.java +++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BlobTest.java @@ -16,7 +16,9 @@ import org.postgresql.test.TestUtil; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.io.InputStream; @@ -35,98 +37,136 @@ * problems from re-occurring ;-) */ public class BlobTest { + private static final String TEST_FILE = "/test-file.xml"; + private static final int LOOP = 0; // LargeObject API using loop private static final int NATIVE_STREAM = 1; // LargeObject API using OutputStream private Connection con; + /* + Only do this once + */ + @BeforeClass + public static void createLargeBlob() throws Exception { + try ( Connection con = TestUtil.openDB() ) { + TestUtil.createTable(con, "testblob", "id name,lo oid"); + con.setAutoCommit(false); + LargeObjectManager lom = ((org.postgresql.PGConnection) con).getLargeObjectAPI(); + long oid = lom.createLO(LargeObjectManager.READWRITE); + LargeObject blob = lom.open(oid); + + byte[] buf = new byte[256]; + for (int i = 0; i < buf.length; i++) { + buf[i] = (byte) i; + } + // I want to create a large object + int i = 1024 / buf.length; + for (int j = i; j > 0; j--) { + blob.write(buf, 0, buf.length); + } + assertEquals(1024, blob.size()); + blob.close(); + try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO testblob(id, lo) VALUES(?,?)")) { + pstmt.setString(1, "l1"); + pstmt.setLong(2, oid); + pstmt.executeUpdate(); + } + con.commit(); + } + } + + @AfterClass + public static void cleanup() throws Exception { + try ( Connection con = TestUtil.openDB() ) { + try (Statement stmt = con.createStatement()) { + stmt.execute("SELECT lo_unlink(lo) FROM testblob where id = 'l1'"); + } finally { + TestUtil.dropTable(con, "testblob"); + } + } + } + @Before public void setUp() throws Exception { con = TestUtil.openDB(); - TestUtil.createTable(con, "testblob", "id name,lo oid"); con.setAutoCommit(false); } @After public void tearDown() throws Exception { con.setAutoCommit(true); - try { - Statement stmt = con.createStatement(); - try { - stmt.execute("SELECT lo_unlink(lo) FROM testblob"); - } finally { - try { - stmt.close(); - } catch (Exception e) { - } - } + try ( Statement stmt = con.createStatement() ) { + stmt.execute("SELECT lo_unlink(lo) FROM testblob where id != 'l1'"); + stmt.execute("delete from testblob where id != 'l1'"); } finally { - TestUtil.dropTable(con, "testblob"); TestUtil.closeDB(con); } } @Test public void testSetNull() throws Exception { - PreparedStatement pstmt = con.prepareStatement("INSERT INTO testblob(lo) VALUES (?)"); + try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO testblob(lo) VALUES (?)")) { - pstmt.setBlob(1, (Blob) null); - pstmt.executeUpdate(); + pstmt.setBlob(1, (Blob) null); + pstmt.executeUpdate(); - pstmt.setNull(1, Types.BLOB); - pstmt.executeUpdate(); + pstmt.setNull(1, Types.BLOB); + pstmt.executeUpdate(); - pstmt.setObject(1, null, Types.BLOB); - pstmt.executeUpdate(); + pstmt.setObject(1, null, Types.BLOB); + pstmt.executeUpdate(); - pstmt.setClob(1, (Clob) null); - pstmt.executeUpdate(); + pstmt.setClob(1, (Clob) null); + pstmt.executeUpdate(); - pstmt.setNull(1, Types.CLOB); - pstmt.executeUpdate(); + pstmt.setNull(1, Types.CLOB); + pstmt.executeUpdate(); - pstmt.setObject(1, null, Types.CLOB); - pstmt.executeUpdate(); + pstmt.setObject(1, null, Types.CLOB); + pstmt.executeUpdate(); + } } @Test public void testSet() throws SQLException { - Statement stmt = con.createStatement(); - stmt.execute("INSERT INTO testblob(id,lo) VALUES ('1', lo_creat(-1))"); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); - - PreparedStatement pstmt = con.prepareStatement("INSERT INTO testblob(id, lo) VALUES(?,?)"); - - Blob blob = rs.getBlob(1); - pstmt.setString(1, "setObjectTypeBlob"); - pstmt.setObject(2, blob, Types.BLOB); - assertEquals(1, pstmt.executeUpdate()); - - blob = rs.getBlob(1); - pstmt.setString(1, "setObjectBlob"); - pstmt.setObject(2, blob); - assertEquals(1, pstmt.executeUpdate()); - - blob = rs.getBlob(1); - pstmt.setString(1, "setBlob"); - pstmt.setBlob(2, blob); - assertEquals(1, pstmt.executeUpdate()); - - Clob clob = rs.getClob(1); - pstmt.setString(1, "setObjectTypeClob"); - pstmt.setObject(2, clob, Types.CLOB); - assertEquals(1, pstmt.executeUpdate()); - - clob = rs.getClob(1); - pstmt.setString(1, "setObjectClob"); - pstmt.setObject(2, clob); - assertEquals(1, pstmt.executeUpdate()); - - clob = rs.getClob(1); - pstmt.setString(1, "setClob"); - pstmt.setClob(2, clob); - assertEquals(1, pstmt.executeUpdate()); + try ( Statement stmt = con.createStatement() ) { + stmt.execute("INSERT INTO testblob(id,lo) VALUES ('1', lo_creat(-1))"); + ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id = '1'"); + assertTrue(rs.next()); + + PreparedStatement pstmt = con.prepareStatement("INSERT INTO testblob(id, lo) VALUES(?,?)"); + + Blob blob = rs.getBlob(1); + pstmt.setString(1, "setObjectTypeBlob"); + pstmt.setObject(2, blob, Types.BLOB); + assertEquals(1, pstmt.executeUpdate()); + + blob = rs.getBlob(1); + pstmt.setString(1, "setObjectBlob"); + pstmt.setObject(2, blob); + assertEquals(1, pstmt.executeUpdate()); + + blob = rs.getBlob(1); + pstmt.setString(1, "setBlob"); + pstmt.setBlob(2, blob); + assertEquals(1, pstmt.executeUpdate()); + + Clob clob = rs.getClob(1); + pstmt.setString(1, "setObjectTypeClob"); + pstmt.setObject(2, clob, Types.CLOB); + assertEquals(1, pstmt.executeUpdate()); + + clob = rs.getClob(1); + pstmt.setString(1, "setObjectClob"); + pstmt.setObject(2, clob); + assertEquals(1, pstmt.executeUpdate()); + + clob = rs.getClob(1); + pstmt.setString(1, "setClob"); + pstmt.setClob(2, clob); + assertEquals(1, pstmt.executeUpdate()); + } } /* @@ -134,13 +174,13 @@ public void testSet() throws SQLException { */ @Test public void testUploadBlob_LOOP() throws Exception { - assertTrue(uploadFile("/test-file.xml", LOOP) > 0); + assertTrue(uploadFile(TEST_FILE, LOOP) > 0); // Now compare the blob & the file. Note this actually tests the // InputStream implementation! - assertTrue(compareBlobsLOAPI()); - assertTrue(compareBlobs()); - assertTrue(compareClobs()); + assertTrue(compareBlobsLOAPI(TEST_FILE)); + assertTrue(compareBlobs(TEST_FILE)); + assertTrue(compareClobs(TEST_FILE)); } /* @@ -148,101 +188,110 @@ public void testUploadBlob_LOOP() throws Exception { */ @Test public void testUploadBlob_NATIVE() throws Exception { - assertTrue(uploadFile("/test-file.xml", NATIVE_STREAM) > 0); + assertTrue(uploadFile(TEST_FILE, NATIVE_STREAM) > 0); // Now compare the blob & the file. Note this actually tests the // InputStream implementation! - assertTrue(compareBlobs()); + assertTrue(compareBlobs(TEST_FILE)); } @Test public void testMarkResetStream() throws Exception { - assertTrue(uploadFile("/test-file.xml", NATIVE_STREAM) > 0); - - Statement stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); - - LargeObjectManager lom = ((org.postgresql.PGConnection) con).getLargeObjectAPI(); - - long oid = rs.getLong(1); - LargeObject blob = lom.open(oid); - InputStream bis = blob.getInputStream(); - - assertEquals('<', bis.read()); - bis.mark(4); - assertEquals('?', bis.read()); - assertEquals('x', bis.read()); - assertEquals('m', bis.read()); - assertEquals('l', bis.read()); - bis.reset(); - assertEquals('?', bis.read()); + assertTrue(uploadFile(TEST_FILE, NATIVE_STREAM) > 0); + + try ( Statement stmt = con.createStatement() ) { + try ( ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id = '/test-file.xml'") ) { + assertTrue(rs.next()); + + LargeObjectManager lom = ((org.postgresql.PGConnection) con).getLargeObjectAPI(); + + long oid = rs.getLong(1); + LargeObject blob = lom.open(oid); + InputStream bis = blob.getInputStream(); + + assertEquals('<', bis.read()); + bis.mark(4); + assertEquals('?', bis.read()); + assertEquals('x', bis.read()); + assertEquals('m', bis.read()); + assertEquals('l', bis.read()); + bis.reset(); + assertEquals('?', bis.read()); + } + } } @Test public void testGetBytesOffset() throws Exception { - assertTrue(uploadFile("/test-file.xml", NATIVE_STREAM) > 0); - - Statement stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); - - Blob lob = rs.getBlob(1); - byte[] data = lob.getBytes(2, 4); - assertEquals(data.length, 4); - assertEquals(data[0], '?'); - assertEquals(data[1], 'x'); - assertEquals(data[2], 'm'); - assertEquals(data[3], 'l'); + assertTrue(uploadFile(TEST_FILE, NATIVE_STREAM) > 0); + + try ( Statement stmt = con.createStatement() ) { + try (ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id = '/test-file.xml'")) { + + assertTrue(rs.next()); + + Blob lob = rs.getBlob(1); + byte[] data = lob.getBytes(2, 4); + assertEquals(data.length, 4); + assertEquals(data[0], '?'); + assertEquals(data[1], 'x'); + assertEquals(data[2], 'm'); + assertEquals(data[3], 'l'); + } + } } @Test public void testMultipleStreams() throws Exception { - assertTrue(uploadFile("/test-file.xml", NATIVE_STREAM) > 0); - - Statement stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); - - Blob lob = rs.getBlob(1); - byte[] data = new byte[2]; - - InputStream is = lob.getBinaryStream(); - assertEquals(data.length, is.read(data)); - assertEquals(data[0], '<'); - assertEquals(data[1], '?'); - is.close(); - - is = lob.getBinaryStream(); - assertEquals(data.length, is.read(data)); - assertEquals(data[0], '<'); - assertEquals(data[1], '?'); - is.close(); + assertTrue(uploadFile(TEST_FILE, NATIVE_STREAM) > 0); + + try ( Statement stmt = con.createStatement() ) { + try (ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id = '/test-file.xml'")) { + assertTrue(rs.next()); + + Blob lob = rs.getBlob(1); + byte[] data = new byte[2]; + + InputStream is = lob.getBinaryStream(); + assertEquals(data.length, is.read(data)); + assertEquals(data[0], '<'); + assertEquals(data[1], '?'); + is.close(); + + is = lob.getBinaryStream(); + assertEquals(data.length, is.read(data)); + assertEquals(data[0], '<'); + assertEquals(data[1], '?'); + is.close(); + } + } } @Test public void testParallelStreams() throws Exception { - assertTrue(uploadFile("/test-file.xml", NATIVE_STREAM) > 0); - - Statement stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); - - Blob lob = rs.getBlob(1); - InputStream is1 = lob.getBinaryStream(); - InputStream is2 = lob.getBinaryStream(); + assertTrue(uploadFile(TEST_FILE, NATIVE_STREAM) > 0); + + try ( Statement stmt = con.createStatement() ) { + try (ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id = '/test-file.xml'")) { + assertTrue(rs.next()); + + Blob lob = rs.getBlob(1); + InputStream is1 = lob.getBinaryStream(); + InputStream is2 = lob.getBinaryStream(); + + while (true) { + int i1 = is1.read(); + int i2 = is2.read(); + assertEquals(i1, i2); + if (i1 == -1) { + break; + } + } - while (true) { - int i1 = is1.read(); - int i2 = is2.read(); - assertEquals(i1, i2); - if (i1 == -1) { - break; + is1.close(); + is2.close(); } } - - is1.close(); - is2.close(); } @Test @@ -251,15 +300,63 @@ public void testLargeLargeObject() throws Exception { return; } - Statement stmt = con.createStatement(); - stmt.execute("INSERT INTO testblob(id,lo) VALUES ('1', lo_creat(-1))"); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); + try ( Statement stmt = con.createStatement() ) { + stmt.execute("INSERT INTO testblob(id,lo) VALUES ('1', lo_creat(-1))"); + try ( ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id ='1'") ) { + assertTrue(rs.next()); - Blob lob = rs.getBlob(1); - long length = ((long) Integer.MAX_VALUE) + 1024; - lob.truncate(length); - assertEquals(length, lob.length()); + Blob lob = rs.getBlob(1); + long length = ((long) Integer.MAX_VALUE) + 1024; + lob.truncate(length); + assertEquals(length, lob.length()); + } + } + } + + @Test + public void testLargeObjectRead() throws Exception { + con.setAutoCommit(false); + LargeObjectManager lom = ((org.postgresql.PGConnection) con).getLargeObjectAPI(); + try ( Statement stmt = con.createStatement() ) { + try ( ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id='l1'") ) { + assertTrue(rs.next()); + + long oid = rs.getLong(1); + InputStream lois = lom.open(oid).getInputStream(); + // read half of the data with read + for ( int j = 0; j < 512; j++ ) { + lois.read(); + } + byte []buf2 = new byte[512]; + lois.read(buf2, 0, 512); + lois.close(); + + } + } + con.commit(); + } + + @Test + public void testLargeObjectRead1() throws Exception { + con.setAutoCommit(false); + LargeObjectManager lom = ((org.postgresql.PGConnection) con).getLargeObjectAPI(); + try ( Statement stmt = con.createStatement() ) { + try ( ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob where id='l1'") ) { + assertTrue(rs.next()); + + long oid = rs.getLong(1); + InputStream lois = lom.open(oid).getInputStream(512, 1024); + // read one byte + assertEquals(0, lois.read()); + byte []buf2 = new byte[1024]; + int bytesRead = lois.read(buf2, 0, buf2.length); + assertEquals( 1023, bytesRead); + assertEquals(1, buf2[0]); + lois.close(); + + } + } + con.commit(); } /* @@ -319,125 +416,123 @@ private long uploadFile(String file, int method) throws Exception { * Helper - compares the blobs in a table with a local file. Note this uses the postgresql * specific Large Object API */ - private boolean compareBlobsLOAPI() throws Exception { + private boolean compareBlobsLOAPI(String id) throws Exception { boolean result = true; LargeObjectManager lom = ((org.postgresql.PGConnection) con).getLargeObjectAPI(); - Statement st = con.createStatement(); - ResultSet rs = st.executeQuery(TestUtil.selectSQL("testblob", "id,lo")); - assertNotNull(rs); - - while (rs.next()) { - String file = rs.getString(1); - long oid = rs.getLong(2); - - InputStream fis = getClass().getResourceAsStream(file); - LargeObject blob = lom.open(oid); - InputStream bis = blob.getInputStream(); - - int f = fis.read(); - int b = bis.read(); - int c = 0; - while (f >= 0 && b >= 0 & result) { - result = (f == b); - f = fis.read(); - b = bis.read(); - c++; - } - result = result && f == -1 && b == -1; - - if (!result) { - fail("Large Object API Blob compare failed at " + c + " of " + blob.size()); + try ( Statement st = con.createStatement() ) { + try ( ResultSet rs = st.executeQuery(TestUtil.selectSQL("testblob", "id,lo", "id = '" + id + "'")) ) { + assertNotNull(rs); + + while (rs.next()) { + String file = rs.getString(1); + long oid = rs.getLong(2); + + InputStream fis = getClass().getResourceAsStream(file); + LargeObject blob = lom.open(oid); + InputStream bis = blob.getInputStream(); + + int f = fis.read(); + int b = bis.read(); + int c = 0; + while (f >= 0 && b >= 0 & result) { + result = (f == b); + f = fis.read(); + b = bis.read(); + c++; + } + result = result && f == -1 && b == -1; + + if (!result) { + fail("Large Object API Blob compare failed at " + c + " of " + blob.size()); + } + + blob.close(); + fis.close(); + } } - - blob.close(); - fis.close(); } - rs.close(); - st.close(); - return result; } /* * Helper - compares the blobs in a table with a local file. This uses the jdbc java.sql.Blob api */ - private boolean compareBlobs() throws Exception { + private boolean compareBlobs(String id) throws Exception { boolean result = true; - Statement st = con.createStatement(); - ResultSet rs = st.executeQuery(TestUtil.selectSQL("testblob", "id,lo")); - assertNotNull(rs); - - while (rs.next()) { - String file = rs.getString(1); - Blob blob = rs.getBlob(2); - - InputStream fis = getClass().getResourceAsStream(file); - InputStream bis = blob.getBinaryStream(); - - int f = fis.read(); - int b = bis.read(); - int c = 0; - while (f >= 0 && b >= 0 & result) { - result = (f == b); - f = fis.read(); - b = bis.read(); - c++; - } - result = result && f == -1 && b == -1; - - if (!result) { - fail("JDBC API Blob compare failed at " + c + " of " + blob.length()); + try ( Statement st = con.createStatement() ) { + try (ResultSet rs = st.executeQuery(TestUtil.selectSQL("testblob", "id,lo", "id = '" + id + "'"))) { + assertNotNull(rs); + + while (rs.next()) { + String file = rs.getString(1); + Blob blob = rs.getBlob(2); + + InputStream fis = getClass().getResourceAsStream(file); + InputStream bis = blob.getBinaryStream(); + + int f = fis.read(); + int b = bis.read(); + int c = 0; + while (f >= 0 && b >= 0 & result) { + result = (f == b); + f = fis.read(); + b = bis.read(); + c++; + } + result = result && f == -1 && b == -1; + + if (!result) { + fail("JDBC API Blob compare failed at " + c + " of " + blob.length()); + } + + bis.close(); + fis.close(); + } } - - bis.close(); - fis.close(); } - rs.close(); - st.close(); - return result; } /* * Helper - compares the clobs in a table with a local file. */ - private boolean compareClobs() throws Exception { + private boolean compareClobs(String id) throws Exception { boolean result = true; - Statement st = con.createStatement(); - ResultSet rs = st.executeQuery(TestUtil.selectSQL("testblob", "id,lo")); - assertNotNull(rs); - - while (rs.next()) { - String file = rs.getString(1); - Clob clob = rs.getClob(2); - - InputStream fis = getClass().getResourceAsStream(file); - InputStream bis = clob.getAsciiStream(); - - int f = fis.read(); - int b = bis.read(); - int c = 0; - while (f >= 0 && b >= 0 & result) { - result = (f == b); - f = fis.read(); - b = bis.read(); - c++; - } - result = result && f == -1 && b == -1; - - if (!result) { - fail("Clob compare failed at " + c + " of " + clob.length()); + try ( Statement st = con.createStatement() ) { + try (ResultSet rs = st.executeQuery(TestUtil.selectSQL("testblob", "id,lo", "id = '" + id + "'"))) { + assertNotNull(rs); + + while (rs.next()) { + String file = rs.getString(1); + Clob clob = rs.getClob(2); + + InputStream fis = getClass().getResourceAsStream(file); + InputStream bis = clob.getAsciiStream(); + + int f = fis.read(); + int b = bis.read(); + int c = 0; + while (f >= 0 && b >= 0 & result) { + result = (f == b); + f = fis.read(); + b = bis.read(); + c++; + } + result = result && f == -1 && b == -1; + + if (!result) { + fail("Clob compare failed at " + c + " of " + clob.length()); + } + + bis.close(); + fis.close(); + } } - - bis.close(); - fis.close(); } - rs.close(); - st.close(); return result; } diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/BlobTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/BlobTest.java index 58ed16e678..77d32b8d11 100644 --- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/BlobTest.java +++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/BlobTest.java @@ -63,25 +63,20 @@ public void tearDown() throws Exception { public void testSetBlobWithStream() throws Exception { byte[] data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque bibendum dapibus varius." .getBytes("UTF-8"); - PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")); - try { + try ( PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")) ) { insertPS.setBlob(1, new ByteArrayInputStream(data)); insertPS.executeUpdate(); - } finally { - insertPS.close(); } - Statement selectStmt = conn.createStatement(); - try { - ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo")); - assertTrue(rs.next()); + try (Statement selectStmt = conn.createStatement() ) { + try (ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo"))) { + assertTrue(rs.next()); - Blob actualBlob = rs.getBlob(1); - byte[] actualBytes = actualBlob.getBytes(1, (int) actualBlob.length()); + Blob actualBlob = rs.getBlob(1); + byte[] actualBytes = actualBlob.getBytes(1, (int) actualBlob.length()); - assertArrayEquals(data, actualBytes); - } finally { - selectStmt.close(); + assertArrayEquals(data, actualBytes); + } } } @@ -91,25 +86,21 @@ public void testSetBlobWithStreamAndLength() throws Exception { .getBytes("UTF-8"); byte[] data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".getBytes("UTF-8"); - PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")); - try { + + try ( PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")) ) { insertPS.setBlob(1, new ByteArrayInputStream(fullData), data.length); insertPS.executeUpdate(); - } finally { - insertPS.close(); } - Statement selectStmt = conn.createStatement(); - try { - ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo")); - assertTrue(rs.next()); + try ( Statement selectStmt = conn.createStatement() ) { + try (ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo"))) { + assertTrue(rs.next()); - Blob actualBlob = rs.getBlob(1); - byte[] actualBytes = actualBlob.getBytes(1, (int) actualBlob.length()); + Blob actualBlob = rs.getBlob(1); + byte[] actualBytes = actualBlob.getBytes(1, (int) actualBlob.length()); - assertArrayEquals(data, actualBytes); - } finally { - selectStmt.close(); + assertArrayEquals(data, actualBytes); + } } } @@ -117,48 +108,75 @@ public void testSetBlobWithStreamAndLength() throws Exception { public void testGetBinaryStreamWithBoundaries() throws Exception { byte[] data = "Cras vestibulum tellus eu sapien imperdiet ornare.".getBytes("UTF-8"); - PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")); - try { + try ( PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")) ) { insertPS.setBlob(1, new ByteArrayInputStream(data), data.length); insertPS.executeUpdate(); - } finally { - insertPS.close(); } + try ( Statement selectStmt = conn.createStatement() ) { + try (ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo"))) { + assertTrue(rs.next()); - Statement selectStmt = conn.createStatement(); - try { - ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo")); - assertTrue(rs.next()); + byte[] actualData = new byte[10]; + Blob actualBlob = rs.getBlob(1); + InputStream stream = actualBlob.getBinaryStream(6, 10); + try { + stream.read(actualData); + assertEquals("Stream should be at end", -1, stream.read(new byte[1])); + } finally { + stream.close(); + } + assertEquals("vestibulum", new String(actualData, "UTF-8")); + } + } + } - byte[] actualData = new byte[10]; - Blob actualBlob = rs.getBlob(1); - InputStream stream = actualBlob.getBinaryStream(6, 10); - try { - stream.read(actualData); - assertEquals("Stream should be at end", -1, stream.read(new byte[1])); - } finally { - stream.close(); + @Test + public void testGetBinaryStreamWithBoundaries2() throws Exception { + byte[] data = + "Cras vestibulum tellus eu sapien imperdiet ornare.".getBytes("UTF-8"); + + try ( PreparedStatement insertPS = conn.prepareStatement(TestUtil.insertSQL("testblob", "lo", "?")) ) { + insertPS.setBlob(1, new ByteArrayInputStream(data), data.length); + insertPS.executeUpdate(); + } + + try ( Statement selectStmt = conn.createStatement() ) { + try (ResultSet rs = selectStmt.executeQuery(TestUtil.selectSQL("testblob", "lo"))) { + assertTrue(rs.next()); + + byte[] actualData = new byte[9]; + Blob actualBlob = rs.getBlob(1); + try ( InputStream stream = actualBlob.getBinaryStream(6, 10) ) { + // read 9 bytes 1 at a time + for (int i = 0; i < 9; i++) { + actualData[i] = (byte) stream.read(); + } + /* try to read past the end and make sure we get 1 byte */ + assertEquals("There should be 1 byte left", 1, stream.read(new byte[2])); + /* now read one more and we should get an EOF */ + assertEquals("Stream should be at end", -1, stream.read(new byte[1])); + } + assertEquals("vestibulu", new String(actualData, "UTF-8")); } - assertEquals("vestibulum", new String(actualData, "UTF-8")); - } finally { - selectStmt.close(); } } @Test public void testFree() throws SQLException { - Statement stmt = conn.createStatement(); - stmt.execute("INSERT INTO testblob(lo) VALUES(lo_creat(-1))"); - ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); - assertTrue(rs.next()); + try ( Statement stmt = conn.createStatement() ) { + stmt.execute("INSERT INTO testblob(lo) VALUES(lo_creat(-1))"); + try (ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob")) { + assertTrue(rs.next()); - Blob blob = rs.getBlob(1); - blob.free(); - try { - blob.length(); - fail("Should have thrown an Exception because it was freed."); - } catch (SQLException sqle) { - // expected + Blob blob = rs.getBlob(1); + blob.free(); + try { + blob.length(); + fail("Should have thrown an Exception because it was freed."); + } catch (SQLException sqle) { + // expected + } + } } } }