1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
package org.postgresql.largeobject;
import java.io.*;
import java.lang.*;
import java.net.*;
import java.util.*;
import java.sql.*;
import org.postgresql.fastpath.*;
/*
* This class implements the large object interface to org.postgresql.
*
* <p>It provides the basic methods required to run the interface, plus
* a pair of methods that provide InputStream and OutputStream classes
* for this object.
*
* <p>Normally, client code would use the getAsciiStream, getBinaryStream,
* or getUnicodeStream methods in ResultSet, or setAsciiStream,
* setBinaryStream, or setUnicodeStream methods in PreparedStatement to
* access Large Objects.
*
* <p>However, sometimes lower level access to Large Objects are required,
* that are not supported by the JDBC specification.
*
* <p>Refer to org.postgresql.largeobject.LargeObjectManager on how to gain access
* to a Large Object, or how to create one.
*
* @see org.postgresql.largeobject.LargeObjectManager
* @see org.postgresql.ResultSet#getAsciiStream
* @see org.postgresql.ResultSet#getBinaryStream
* @see org.postgresql.ResultSet#getUnicodeStream
* @see org.postgresql.PreparedStatement#setAsciiStream
* @see org.postgresql.PreparedStatement#setBinaryStream
* @see org.postgresql.PreparedStatement#setUnicodeStream
* @see java.sql.ResultSet#getAsciiStream
* @see java.sql.ResultSet#getBinaryStream
* @see java.sql.ResultSet#getUnicodeStream
* @see java.sql.PreparedStatement#setAsciiStream
* @see java.sql.PreparedStatement#setBinaryStream
* @see java.sql.PreparedStatement#setUnicodeStream
*
*/
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
private BlobOutputStream os; // The current output stream
private boolean closed = false; // true when we are closed
/*
* This opens a large object.
*
* <p>If the object does not exist, then an SQLException is thrown.
*
* @param fp FastPath API for the connection to use
* @param oid of the Large Object to open
* @param mode Mode of opening the large object
* (defined in LargeObjectManager)
* @exception SQLException if a database-access error occurs.
* @see org.postgresql.largeobject.LargeObjectManager
*/
protected LargeObject(Fastpath fp, int oid, int mode) throws SQLException
{
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);
}
/* Release large object resources during garbage cleanup */
protected void finalize() throws SQLException
{
//This code used to call close() however that was problematic
//because the scope of the fd is a transaction, thus if commit
//or rollback was called before garbage collection ran then
//the call to close would error out with an invalid large object
//handle. So this method now does nothing and lets the server
//handle cleanup when it ends the transaction.
}
/*
* @return the OID of this LargeObject
*/
public int getOID()
{
return oid;
}
/*
* This method closes the object. You must not call methods in this
* object after this is called.
* @exception SQLException if a database-access error occurs.
*/
public void close() throws SQLException
{
if (!closed)
{
// flush any open output streams
if (os != null)
{
try
{
// we can't call os.close() otherwise we go into an infinite loop!
os.flush();
}
catch (IOException ioe)
{
throw new SQLException(ioe.getMessage());
}
finally
{
os = null;
}
}
// finally close
FastpathArg args[] = new FastpathArg[1];
args[0] = new FastpathArg(fd);
fp.fastpath("lo_close", false, args); // true here as we dont care!!
closed = true;
}
}
/*
* Reads some data from the object, and return as a byte[] array
*
* @param len number of bytes to read
* @return byte[] array containing data read
* @exception SQLException if a database-access error occurs.
*/
public byte[] read(int len) throws SQLException
{
// This is the original method, where the entire block (len bytes)
// is retrieved in one go.
FastpathArg args[] = new FastpathArg[2];
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
//FastpathArg args[] = new FastpathArg[2];
//args[0] = new FastpathArg(fd);
//args[1] = new FastpathArg(len);
//return fp.getData("loread",args);
//} else {
//// return in 4k blocks
//byte[] buf=new byte[len];
//int off=0;
//while (len>0) {
//int bs=4048;
//len-=bs;
//if (len<0) {
//bs+=len;
//len=0;
//}
//read(buf,off,bs);
//off+=bs;
//}
//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 int read(byte buf[], int off, int len) throws SQLException
{
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
*
* @param buf array to write
* @exception SQLException if a database-access error occurs.
*/
public void write(byte buf[]) throws SQLException
{
FastpathArg args[] = new FastpathArg[2];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(buf);
fp.fastpath("lowrite", false, args);
}
/*
* Writes some data from an array to the object
*
* @param buf destination array
* @param off offset within array
* @param len number of bytes to write
* @exception SQLException if a database-access error occurs.
*/
public void write(byte buf[], int off, int len) throws SQLException
{
byte data[] = new byte[len];
System.arraycopy(buf, off, data, 0, len);
write(data);
}
/*
* Sets the current position within the object.
*
* <p>This is similar to the fseek() call in the standard C library. It
* allows you to have random access to the large object.
*
* @param pos position within object
* @param ref Either SEEK_SET, SEEK_CUR or SEEK_END
* @exception SQLException if a database-access error occurs.
*/
public void seek(int pos, int ref) throws SQLException
{
FastpathArg args[] = new FastpathArg[3];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(pos);
args[2] = new FastpathArg(ref);
fp.fastpath("lo_lseek", false, args);
}
/*
* Sets the current position within the object.
*
* <p>This is similar to the fseek() call in the standard C library. It
* allows you to have random access to the large object.
*
* @param pos position within object from begining
* @exception SQLException if a database-access error occurs.
*/
public void seek(int pos) throws SQLException
{
seek(pos, SEEK_SET);
}
/*
* @return the current position within the object
* @exception SQLException if a database-access error occurs.
*/
public int tell() throws SQLException
{
FastpathArg args[] = new FastpathArg[1];
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
* return to the original position.
*
* <p>A better method will be found in the future.
*
* @return the size of the large object
* @exception SQLException if a database-access error occurs.
*/
public int size() throws SQLException
{
int cp = tell();
seek(0, SEEK_END);
int sz = tell();
seek(cp, SEEK_SET);
return sz;
}
/*
* Returns an InputStream from this object.
*
* <p>This InputStream can then be used in any method that requires an
* InputStream.
*
* @exception SQLException if a database-access error occurs.
*/
public InputStream getInputStream() throws SQLException
{
return new BlobInputStream(this);
}
/*
* Returns an OutputStream to this object
*
* <p>This OutputStream can then be used in any method that requires an
* OutputStream.
*
* @exception SQLException if a database-access error occurs.
*/
public OutputStream getOutputStream() throws SQLException
{
if (os == null)
os = new BlobOutputStream(this);
return os;
}
}