001: /*-------------------------------------------------------------------------
002: *
003: * Copyright (c) 2003-2005, PostgreSQL Global Development Group
004: *
005: * IDENTIFICATION
006: * $PostgreSQL: pgjdbc/org/postgresql/largeobject/LargeObject.java,v 1.21 2007/07/27 23:09:38 jurka Exp $
007: *
008: *-------------------------------------------------------------------------
009: */
010: package org.postgresql.largeobject;
011:
012: import java.io.InputStream;
013: import java.io.IOException;
014: import java.io.OutputStream;
015: import java.sql.SQLException;
016: import org.postgresql.util.PSQLException;
017: import org.postgresql.util.PSQLState;
018: import org.postgresql.fastpath.Fastpath;
019: import org.postgresql.fastpath.FastpathArg;
020:
021: /**
022: * This class provides the basic methods required to run the interface, plus
023: * a pair of methods that provide InputStream and OutputStream classes
024: * for this object.
025: *
026: * <p>Normally, client code would use the getAsciiStream, getBinaryStream,
027: * or getUnicodeStream methods in ResultSet, or setAsciiStream,
028: * setBinaryStream, or setUnicodeStream methods in PreparedStatement to
029: * access Large Objects.
030: *
031: * <p>However, sometimes lower level access to Large Objects are required,
032: * that are not supported by the JDBC specification.
033: *
034: * <p>Refer to org.postgresql.largeobject.LargeObjectManager on how to gain access
035: * to a Large Object, or how to create one.
036: *
037: * @see org.postgresql.largeobject.LargeObjectManager
038: * @see java.sql.ResultSet#getAsciiStream
039: * @see java.sql.ResultSet#getBinaryStream
040: * @see java.sql.ResultSet#getUnicodeStream
041: * @see java.sql.PreparedStatement#setAsciiStream
042: * @see java.sql.PreparedStatement#setBinaryStream
043: * @see java.sql.PreparedStatement#setUnicodeStream
044: *
045: */
046: public class LargeObject {
047: /**
048: * Indicates a seek from the begining of a file
049: */
050: public static final int SEEK_SET = 0;
051:
052: /**
053: * Indicates a seek from the current position
054: */
055: public static final int SEEK_CUR = 1;
056:
057: /**
058: * Indicates a seek from the end of a file
059: */
060: public static final int SEEK_END = 2;
061:
062: private final Fastpath fp; // Fastpath API to use
063: private final long oid; // OID of this object
064: private final int mode; // read/write mode of this object
065: private final int fd; // the descriptor of the open large object
066:
067: private BlobOutputStream os; // The current output stream
068:
069: private boolean closed = false; // true when we are closed
070:
071: /**
072: * This opens a large object.
073: *
074: * <p>If the object does not exist, then an SQLException is thrown.
075: *
076: * @param fp FastPath API for the connection to use
077: * @param oid of the Large Object to open
078: * @param mode Mode of opening the large object
079: * (defined in LargeObjectManager)
080: * @exception SQLException if a database-access error occurs.
081: * @see org.postgresql.largeobject.LargeObjectManager
082: */
083: protected LargeObject(Fastpath fp, long oid, int mode)
084: throws SQLException {
085: this .fp = fp;
086: this .oid = oid;
087: this .mode = mode;
088:
089: FastpathArg args[] = new FastpathArg[2];
090: args[0] = Fastpath.createOIDArg(oid);
091: args[1] = new FastpathArg(mode);
092: this .fd = fp.getInteger("lo_open", args);
093: }
094:
095: public LargeObject copy() throws SQLException {
096: return new LargeObject(fp, oid, mode);
097: }
098:
099: /* Release large object resources during garbage cleanup.
100: *
101: * This code used to call close() however that was problematic
102: * because the scope of the fd is a transaction, thus if commit
103: * or rollback was called before garbage collection ran then
104: * the call to close would error out with an invalid large object
105: * handle. So this method now does nothing and lets the server
106: * handle cleanup when it ends the transaction.
107: *
108: * protected void finalize() throws SQLException
109: * {
110: * }
111: */
112:
113: /**
114: * @return the OID of this LargeObject
115: * @deprecated As of 8.3, replaced by {@link #getLongOID()}
116: */
117: public int getOID() {
118: return (int) oid;
119: }
120:
121: /**
122: * @return the OID of this LargeObject
123: */
124: public long getLongOID() {
125: return oid;
126: }
127:
128: /**
129: * This method closes the object. You must not call methods in this
130: * object after this is called.
131: * @exception SQLException if a database-access error occurs.
132: */
133: public void close() throws SQLException {
134: if (!closed) {
135: // flush any open output streams
136: if (os != null) {
137: try {
138: // we can't call os.close() otherwise we go into an infinite loop!
139: os.flush();
140: } catch (IOException ioe) {
141: throw new PSQLException(
142: "Exception flushing output stream",
143: PSQLState.DATA_ERROR, ioe);
144: } finally {
145: os = null;
146: }
147: }
148:
149: // finally close
150: FastpathArg args[] = new FastpathArg[1];
151: args[0] = new FastpathArg(fd);
152: fp.fastpath("lo_close", false, args); // true here as we dont care!!
153: closed = true;
154: }
155: }
156:
157: /**
158: * Reads some data from the object, and return as a byte[] array
159: *
160: * @param len number of bytes to read
161: * @return byte[] array containing data read
162: * @exception SQLException if a database-access error occurs.
163: */
164: public byte[] read(int len) throws SQLException {
165: // This is the original method, where the entire block (len bytes)
166: // is retrieved in one go.
167: FastpathArg args[] = new FastpathArg[2];
168: args[0] = new FastpathArg(fd);
169: args[1] = new FastpathArg(len);
170: return fp.getData("loread", args);
171: }
172:
173: /**
174: * Reads some data from the object into an existing array
175: *
176: * @param buf destination array
177: * @param off offset within array
178: * @param len number of bytes to read
179: * @return the number of bytes actually read
180: * @exception SQLException if a database-access error occurs.
181: */
182: public int read(byte buf[], int off, int len) throws SQLException {
183: byte b[] = read(len);
184: if (b.length < len)
185: len = b.length;
186: System.arraycopy(b, 0, buf, off, len);
187: return len;
188: }
189:
190: /**
191: * Writes an array to the object
192: *
193: * @param buf array to write
194: * @exception SQLException if a database-access error occurs.
195: */
196: public void write(byte buf[]) throws SQLException {
197: FastpathArg args[] = new FastpathArg[2];
198: args[0] = new FastpathArg(fd);
199: args[1] = new FastpathArg(buf);
200: fp.fastpath("lowrite", false, args);
201: }
202:
203: /**
204: * Writes some data from an array to the object
205: *
206: * @param buf destination array
207: * @param off offset within array
208: * @param len number of bytes to write
209: * @exception SQLException if a database-access error occurs.
210: */
211: public void write(byte buf[], int off, int len) throws SQLException {
212: FastpathArg args[] = new FastpathArg[2];
213: args[0] = new FastpathArg(fd);
214: args[1] = new FastpathArg(buf, off, len);
215: fp.fastpath("lowrite", false, args);
216: }
217:
218: /**
219: * Sets the current position within the object.
220: *
221: * <p>This is similar to the fseek() call in the standard C library. It
222: * allows you to have random access to the large object.
223: *
224: * @param pos position within object
225: * @param ref Either SEEK_SET, SEEK_CUR or SEEK_END
226: * @exception SQLException if a database-access error occurs.
227: */
228: public void seek(int pos, int ref) throws SQLException {
229: FastpathArg args[] = new FastpathArg[3];
230: args[0] = new FastpathArg(fd);
231: args[1] = new FastpathArg(pos);
232: args[2] = new FastpathArg(ref);
233: fp.fastpath("lo_lseek", false, args);
234: }
235:
236: /**
237: * Sets the current position within the object.
238: *
239: * <p>This is similar to the fseek() call in the standard C library. It
240: * allows you to have random access to the large object.
241: *
242: * @param pos position within object from begining
243: * @exception SQLException if a database-access error occurs.
244: */
245: public void seek(int pos) throws SQLException {
246: seek(pos, SEEK_SET);
247: }
248:
249: /**
250: * @return the current position within the object
251: * @exception SQLException if a database-access error occurs.
252: */
253: public int tell() throws SQLException {
254: FastpathArg args[] = new FastpathArg[1];
255: args[0] = new FastpathArg(fd);
256: return fp.getInteger("lo_tell", args);
257: }
258:
259: /**
260: * This method is inefficient, as the only way to find out the size of
261: * the object is to seek to the end, record the current position, then
262: * return to the original position.
263: *
264: * <p>A better method will be found in the future.
265: *
266: * @return the size of the large object
267: * @exception SQLException if a database-access error occurs.
268: */
269: public int size() throws SQLException {
270: int cp = tell();
271: seek(0, SEEK_END);
272: int sz = tell();
273: seek(cp, SEEK_SET);
274: return sz;
275: }
276:
277: /**
278: * Truncates the large object to the given length in bytes.
279: * If the number of bytes is larger than the current large
280: * object length, the large object will be filled with zero
281: * bytes. This method does not modify the current file offset.
282: */
283: public void truncate(int len) throws SQLException {
284: FastpathArg args[] = new FastpathArg[2];
285: args[0] = new FastpathArg(fd);
286: args[1] = new FastpathArg(len);
287: fp.getInteger("lo_truncate", args);
288: }
289:
290: /**
291: * Returns an InputStream from this object.
292: *
293: * <p>This InputStream can then be used in any method that requires an
294: * InputStream.
295: *
296: * @exception SQLException if a database-access error occurs.
297: */
298: public InputStream getInputStream() throws SQLException {
299: return new BlobInputStream(this , 4096);
300: }
301:
302: /**
303: * Returns an OutputStream to this object.
304: *
305: * <p>This OutputStream can then be used in any method that requires an
306: * OutputStream.
307: *
308: * @exception SQLException if a database-access error occurs.
309: */
310: public OutputStream getOutputStream() throws SQLException {
311: if (os == null)
312: os = new BlobOutputStream(this , 4096);
313: return os;
314: }
315:
316: }
|