mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +03:00
Tue Jan 30 22:24:00 GMT 2001 peter@retep.org.uk
- Fixed bug where Statement.setMaxRows() was a global setting. Now limited to just itself. - Changed LargeObject.read(byte[],int,int) to return the actual number of bytes read (used to be void). - LargeObject now supports InputStream's! - PreparedStatement.setBinaryStream() now works! - ResultSet.getBinaryStream() now returns an InputStream that doesn't copy the blob into memory first! - Connection.isClosed() now tests to see if the connection is still alive rather than if it thinks it's alive.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
package org.postgresql.largeobject;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* This is an initial implementation of an InputStream from a large object.
|
||||
* For now, the bare minimum is implemented. Later (after 7.1) we will overide
|
||||
* the other read methods to optimise them.
|
||||
*/
|
||||
public class BlobInputStream extends InputStream {
|
||||
/**
|
||||
* The parent LargeObject
|
||||
*/
|
||||
private LargeObject lo;
|
||||
|
||||
/**
|
||||
* Buffer used to improve performance
|
||||
*/
|
||||
private byte[] buffer;
|
||||
|
||||
/**
|
||||
* Position within buffer
|
||||
*/
|
||||
private int bpos;
|
||||
|
||||
/**
|
||||
* The buffer size
|
||||
*/
|
||||
private int bsize;
|
||||
|
||||
/**
|
||||
* The mark position
|
||||
*/
|
||||
private int mpos=0;
|
||||
|
||||
/**
|
||||
* @param lo LargeObject to read from
|
||||
*/
|
||||
public BlobInputStream(LargeObject lo) {
|
||||
this(lo,1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lo LargeObject to read from
|
||||
* @param bsize buffer size
|
||||
*/
|
||||
public BlobInputStream(LargeObject lo,int bsize) {
|
||||
this.lo=lo;
|
||||
buffer=null;
|
||||
bpos=0;
|
||||
this.bsize=bsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
return (int) buffer[bpos++];
|
||||
} catch(SQLException se) {
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closes this input stream and releases any system resources associated
|
||||
* with the stream.
|
||||
*
|
||||
* <p> The <code>close</code> method of <code>InputStream</code> does
|
||||
* nothing.
|
||||
*
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
lo.close();
|
||||
lo=null;
|
||||
} catch(SQLException se) {
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current position in this input stream. A subsequent call to
|
||||
* the <code>reset</code> method repositions this stream at the last marked
|
||||
* position so that subsequent reads re-read the same bytes.
|
||||
*
|
||||
* <p> The <code>readlimit</code> arguments tells this input stream to
|
||||
* allow that many bytes to be read before the mark position gets
|
||||
* invalidated.
|
||||
*
|
||||
* <p> The general contract of <code>mark</code> is that, if the method
|
||||
* <code>markSupported</code> returns <code>true</code>, the stream somehow
|
||||
* remembers all the bytes read after the call to <code>mark</code> and
|
||||
* stands ready to supply those same bytes again if and whenever the method
|
||||
* <code>reset</code> is called. However, the stream is not required to
|
||||
* remember any data at all if more than <code>readlimit</code> bytes are
|
||||
* read from the stream before <code>reset</code> is called.
|
||||
*
|
||||
* <p> The <code>mark</code> method of <code>InputStream</code> does
|
||||
* nothing.
|
||||
*
|
||||
* @param readlimit the maximum limit of bytes that can be read before
|
||||
* the mark position becomes invalid.
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
public synchronized void mark(int readlimit) {
|
||||
try {
|
||||
mpos=lo.tell();
|
||||
} catch(SQLException se) {
|
||||
//throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repositions this stream to the position at the time the
|
||||
* <code>mark</code> method was last called on this input stream.
|
||||
* NB: If mark is not called we move to the begining.
|
||||
* @see java.io.InputStream#mark(int)
|
||||
* @see java.io.IOException
|
||||
*/
|
||||
public synchronized void reset() throws IOException {
|
||||
try {
|
||||
lo.seek(mpos);
|
||||
} catch(SQLException se) {
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this input stream supports the <code>mark</code> and
|
||||
* <code>reset</code> methods. The <code>markSupported</code> method of
|
||||
* <code>InputStream</code> returns <code>false</code>.
|
||||
*
|
||||
* @return <code>true</code> if this true type supports the mark and reset
|
||||
* method; <code>false</code> otherwise.
|
||||
* @see java.io.InputStream#mark(int)
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
package org.postgresql.largeobject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* This implements a basic output stream that writes to a LargeObject
|
||||
*/
|
||||
public class BlobOutputStream extends OutputStream {
|
||||
/**
|
||||
* The parent LargeObject
|
||||
*/
|
||||
private LargeObject lo;
|
||||
|
||||
/**
|
||||
* Buffer
|
||||
*/
|
||||
private byte buf[];
|
||||
|
||||
/**
|
||||
* Size of the buffer (default 1K)
|
||||
*/
|
||||
private int bsize;
|
||||
|
||||
/**
|
||||
* Position within the buffer
|
||||
*/
|
||||
private int bpos;
|
||||
|
||||
/**
|
||||
* Create an OutputStream to a large object
|
||||
* @param lo LargeObject
|
||||
*/
|
||||
public BlobOutputStream(LargeObject lo) {
|
||||
this(lo,1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OutputStream to a large object
|
||||
* @param lo LargeObject
|
||||
* @param bsize The size of the buffer used to improve performance
|
||||
*/
|
||||
public BlobOutputStream(LargeObject lo,int bsize) {
|
||||
this.lo=lo;
|
||||
this.bsize=bsize;
|
||||
buf=new byte[bsize];
|
||||
bpos=0;
|
||||
}
|
||||
|
||||
public void write(int b) throws java.io.IOException {
|
||||
try {
|
||||
if(bpos>=bsize) {
|
||||
lo.write(buf);
|
||||
bpos=0;
|
||||
}
|
||||
buf[bpos++]=(byte)b;
|
||||
} catch(SQLException se) {
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes this output stream and forces any buffered output bytes
|
||||
* to be written out. The general contract of <code>flush</code> is
|
||||
* that calling it is an indication that, if any bytes previously
|
||||
* written have been buffered by the implementation of the output
|
||||
* stream, such bytes should immediately be written to their
|
||||
* intended destination.
|
||||
*
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
try {
|
||||
if(bpos>0)
|
||||
lo.write(buf,0,bpos);
|
||||
bpos=0;
|
||||
} catch(SQLException se) {
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this output stream and releases any system resources
|
||||
* associated with this stream. The general contract of <code>close</code>
|
||||
* is that it closes the output stream. A closed stream cannot perform
|
||||
* output operations and cannot be reopened.
|
||||
* <p>
|
||||
* The <code>close</code> method of <code>OutputStream</code> does nothing.
|
||||
*
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
lo.close();
|
||||
lo=null;
|
||||
} catch(SQLException se) {
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -16,7 +16,7 @@ import org.postgresql.fastpath.*;
|
||||
* for this object.
|
||||
*
|
||||
* <p>Normally, client code would use the getAsciiStream, getBinaryStream,
|
||||
* or getUnicodeStream methods in ResultSet, or setAsciiStream,
|
||||
* or getUnicodeStream methods in ResultSet, or setAsciiStream,
|
||||
* setBinaryStream, or setUnicodeStream methods in PreparedStatement to
|
||||
* access Large Objects.
|
||||
*
|
||||
@@ -47,21 +47,21 @@ public class LargeObject
|
||||
* Indicates a seek from the begining of a file
|
||||
*/
|
||||
public static final int SEEK_SET = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a seek from the current position
|
||||
*/
|
||||
public static final int SEEK_CUR = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a seek from the end of a file
|
||||
*/
|
||||
public static final int SEEK_END = 2;
|
||||
|
||||
|
||||
private Fastpath fp; // Fastpath API to use
|
||||
private int oid; // OID of this object
|
||||
private int fd; // the descriptor of the open large object
|
||||
|
||||
|
||||
/**
|
||||
* This opens a large object.
|
||||
*
|
||||
@@ -78,13 +78,13 @@ public class LargeObject
|
||||
{
|
||||
this.fp = fp;
|
||||
this.oid = oid;
|
||||
|
||||
|
||||
FastpathArg args[] = new FastpathArg[2];
|
||||
args[0] = new FastpathArg(oid);
|
||||
args[1] = new FastpathArg(mode);
|
||||
this.fd = fp.getInteger("lo_open",args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the OID of this LargeObject
|
||||
*/
|
||||
@@ -92,7 +92,7 @@ public class LargeObject
|
||||
{
|
||||
return oid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method closes the object. You must not call methods in this
|
||||
* object after this is called.
|
||||
@@ -104,7 +104,7 @@ public class LargeObject
|
||||
args[0] = new FastpathArg(fd);
|
||||
fp.fastpath("lo_close",false,args); // true here as we dont care!!
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads some data from the object, and return as a byte[] array
|
||||
*
|
||||
@@ -120,7 +120,7 @@ public class LargeObject
|
||||
args[0] = new FastpathArg(fd);
|
||||
args[1] = new FastpathArg(len);
|
||||
return fp.getData("loread",args);
|
||||
|
||||
|
||||
// This version allows us to break this down into 4k blocks
|
||||
//if(len<=4048) {
|
||||
//// handle as before, return the whole block in one go
|
||||
@@ -145,20 +145,25 @@ public class LargeObject
|
||||
//return buf;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads some data from the object into an existing array
|
||||
*
|
||||
* @param buf destination array
|
||||
* @param off offset within array
|
||||
* @param len number of bytes to read
|
||||
* @return the number of bytes actually read
|
||||
* @exception SQLException if a database-access error occurs.
|
||||
*/
|
||||
public void read(byte buf[],int off,int len) throws SQLException
|
||||
public int read(byte buf[],int off,int len) throws SQLException
|
||||
{
|
||||
System.arraycopy(read(len),0,buf,off,len);
|
||||
byte b[] = read(len);
|
||||
if(b.length<len)
|
||||
len=b.length;
|
||||
System.arraycopy(b,0,buf,off,len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes an array to the object
|
||||
*
|
||||
@@ -172,7 +177,7 @@ public class LargeObject
|
||||
args[1] = new FastpathArg(buf);
|
||||
fp.fastpath("lowrite",false,args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes some data from an array to the object
|
||||
*
|
||||
@@ -187,7 +192,7 @@ public class LargeObject
|
||||
System.arraycopy(buf,off,data,0,len);
|
||||
write(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current position within the object.
|
||||
*
|
||||
@@ -206,7 +211,7 @@ public class LargeObject
|
||||
args[2] = new FastpathArg(ref);
|
||||
fp.fastpath("lo_lseek",false,args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current position within the object.
|
||||
*
|
||||
@@ -220,7 +225,7 @@ public class LargeObject
|
||||
{
|
||||
seek(pos,SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the current position within the object
|
||||
* @exception SQLException if a database-access error occurs.
|
||||
@@ -231,7 +236,7 @@ public class LargeObject
|
||||
args[0] = new FastpathArg(fd);
|
||||
return fp.getInteger("lo_tell",args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is inefficient, as the only way to find out the size of
|
||||
* the object is to seek to the end, record the current position, then
|
||||
@@ -250,7 +255,7 @@ public class LargeObject
|
||||
seek(cp,SEEK_SET);
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an InputStream from this object.
|
||||
*
|
||||
@@ -261,9 +266,9 @@ public class LargeObject
|
||||
*/
|
||||
public InputStream getInputStream() throws SQLException
|
||||
{
|
||||
throw org.postgresql.Driver.notImplemented();
|
||||
return new BlobInputStream(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an OutputStream to this object
|
||||
*
|
||||
@@ -274,6 +279,7 @@ public class LargeObject
|
||||
*/
|
||||
public OutputStream getOutputStream() throws SQLException
|
||||
{
|
||||
throw org.postgresql.Driver.notImplemented();
|
||||
return new BlobOutputStream(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user