Blob.java
13.4 KB
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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*
Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
this software, see the FOSS License Exception
<http://www.mysql.com/about/legal/licensing/foss-exception.html>.
This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; version 2
of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.jdbc;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
/**
* The representation (mapping) in the JavaTM programming language of an SQL BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object
* as a column value in a row of a database table. The driver implements Blob using an SQL locator(BLOB), which means that a Blob object contains a logical
* pointer to the SQL BLOB data rather than the data itself. A Blob object is valid for the duration of the transaction in which is was created. Methods in
* the interfaces ResultSet, CallableStatement, and PreparedStatement, such as getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob
* interface provides methods for getting the length of an SQL BLOB (Binary Large Object) value, for materializing a BLOB value on the client, and for
* determining the position of a pattern of bytes within a BLOB value. This class is new in the JDBC 2.0 API.
*/
public class Blob implements java.sql.Blob, OutputStreamWatcher {
//
// This is a real brain-dead implementation of BLOB. Once I add streamability to the I/O for MySQL this will be more efficiently implemented
// (except for the position() method, ugh).
//
/** The binary data that makes up this BLOB */
private byte[] binaryData = null;
private boolean isClosed = false;
private ExceptionInterceptor exceptionInterceptor;
/**
* Creates a Blob without data
*/
Blob(ExceptionInterceptor exceptionInterceptor) {
setBinaryData(Constants.EMPTY_BYTE_ARRAY);
this.exceptionInterceptor = exceptionInterceptor;
}
/**
* Creates a BLOB encapsulating the given binary data
*
* @param data
*/
Blob(byte[] data, ExceptionInterceptor exceptionInterceptor) {
setBinaryData(data);
this.exceptionInterceptor = exceptionInterceptor;
}
/**
* Creates an updatable BLOB that can update in-place (not implemented yet).
*
* @param data
* @param creatorResultSetToSet
* @param columnIndexToSet
*/
Blob(byte[] data, ResultSetInternalMethods creatorResultSetToSet, int columnIndexToSet) {
setBinaryData(data);
}
private synchronized byte[] getBinaryData() {
return this.binaryData;
}
/**
* Retrieves the BLOB designated by this Blob instance as a stream.
*
* @return this BLOB represented as a binary stream of bytes.
*
* @throws SQLException
* if a database error occurs
*/
public synchronized java.io.InputStream getBinaryStream() throws SQLException {
checkClosed();
return new ByteArrayInputStream(getBinaryData());
}
/**
* Returns as an array of bytes, part or all of the BLOB value that this
* Blob object designates.
*
* @param pos
* where to start the part of the BLOB
* @param length
* the length of the part of the BLOB you want returned.
*
* @return the bytes stored in the blob starting at position <code>pos</code> and having a length of <code>length</code>.
*
* @throws SQLException
* if a database error occurs
*/
public synchronized byte[] getBytes(long pos, int length) throws SQLException {
checkClosed();
if (pos < 1) {
throw SQLError.createSQLException(Messages.getString("Blob.2"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
pos--;
if (pos > this.binaryData.length) {
throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
this.exceptionInterceptor);
}
if (pos + length > this.binaryData.length) {
throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
this.exceptionInterceptor);
}
byte[] newData = new byte[length];
System.arraycopy(getBinaryData(), (int) (pos), newData, 0, length);
return newData;
}
/**
* Returns the number of bytes in the BLOB value designated by this Blob
* object.
*
* @return the length of this blob
*
* @throws SQLException
* if a database error occurs
*/
public synchronized long length() throws SQLException {
checkClosed();
return getBinaryData().length;
}
/**
* @see java.sql.Blob#position(byte[], long)
*/
public synchronized long position(byte[] pattern, long start) throws SQLException {
throw SQLError.createSQLException("Not implemented", this.exceptionInterceptor);
}
/**
* Finds the position of the given pattern in this BLOB.
*
* @param pattern
* the pattern to find
* @param start
* where to start finding the pattern
*
* @return the position where the pattern is found in the BLOB, -1 if not
* found
*
* @throws SQLException
* if a database error occurs
*/
public synchronized long position(java.sql.Blob pattern, long start) throws SQLException {
checkClosed();
return position(pattern.getBytes(0, (int) pattern.length()), start);
}
private synchronized void setBinaryData(byte[] newBinaryData) {
this.binaryData = newBinaryData;
}
/**
* @see Blob#setBinaryStream(long)
*/
public synchronized OutputStream setBinaryStream(long indexToWriteAt) throws SQLException {
checkClosed();
if (indexToWriteAt < 1) {
throw SQLError.createSQLException(Messages.getString("Blob.0"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
WatchableOutputStream bytesOut = new WatchableOutputStream();
bytesOut.setWatcher(this);
if (indexToWriteAt > 0) {
bytesOut.write(this.binaryData, 0, (int) (indexToWriteAt - 1));
}
return bytesOut;
}
/**
* @see Blob#setBytes(long, byte[])
*/
public synchronized int setBytes(long writeAt, byte[] bytes) throws SQLException {
checkClosed();
return setBytes(writeAt, bytes, 0, bytes.length);
}
/**
* @see Blob#setBytes(long, byte[], int, int)
*/
public synchronized int setBytes(long writeAt, byte[] bytes, int offset, int length) throws SQLException {
checkClosed();
OutputStream bytesOut = setBinaryStream(writeAt);
try {
bytesOut.write(bytes, offset, length);
} catch (IOException ioEx) {
SQLException sqlEx = SQLError.createSQLException(Messages.getString("Blob.1"), SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor);
sqlEx.initCause(ioEx);
throw sqlEx;
} finally {
try {
bytesOut.close();
} catch (IOException doNothing) {
// do nothing
}
}
return length;
}
/**
* @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
*/
public synchronized void streamClosed(byte[] byteData) {
this.binaryData = byteData;
}
/**
* @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
*/
public synchronized void streamClosed(WatchableOutputStream out) {
int streamSize = out.size();
if (streamSize < this.binaryData.length) {
out.write(this.binaryData, streamSize, this.binaryData.length - streamSize);
}
this.binaryData = out.toByteArray();
}
/**
* Truncates the <code>BLOB</code> value that this <code>Blob</code> object represents to be <code>len</code> bytes in length.
* <p>
* <b>Note:</b> If the value specified for <code>len</code> is greater then the length+1 of the <code>BLOB</code> value then the behavior is undefined. Some
* JDBC drivers may throw a <code>SQLException</code> while other drivers may support this operation.
*
* @param len
* the length, in bytes, to which the <code>BLOB</code> value
* that this <code>Blob</code> object represents should be truncated
* @exception SQLException
* if there is an error accessing the <code>BLOB</code> value or if len is less than 0
* @exception SQLFeatureNotSupportedException
* if the JDBC driver does not support
* this method
* @since 1.4
*/
public synchronized void truncate(long len) throws SQLException {
checkClosed();
if (len < 0) {
throw SQLError.createSQLException("\"len\" argument can not be < 1.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
if (len > this.binaryData.length) {
throw SQLError.createSQLException("\"len\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
this.exceptionInterceptor);
}
// TODO: Do this without copying byte[]s by maintaining some end pointer on the original data
byte[] newData = new byte[(int) len];
System.arraycopy(getBinaryData(), 0, newData, 0, (int) len);
this.binaryData = newData;
}
/**
* This method frees the <code>Blob</code> object and releases the resources that
* it holds. The object is invalid once the <code>free</code> method is called.
* <p>
* After <code>free</code> has been called, any attempt to invoke a method other than <code>free</code> will result in a <code>SQLException</code> being
* thrown. If <code>free</code> is called multiple times, the subsequent calls to <code>free</code> are treated as a no-op.
* <p>
*
* @throws SQLException
* if an error occurs releasing
* the Blob's resources
* @exception SQLFeatureNotSupportedException
* if the JDBC driver does not support
* this method
* @since 1.6
*/
public synchronized void free() throws SQLException {
this.binaryData = null;
this.isClosed = true;
}
/**
* Returns an <code>InputStream</code> object that contains a partial <code>Blob</code> value,
* starting with the byte specified by pos, which is length bytes in length.
*
* @param pos
* the offset to the first byte of the partial value to be retrieved.
* The first byte in the <code>Blob</code> is at position 1
* @param length
* the length in bytes of the partial value to be retrieved
* @return <code>InputStream</code> through which the partial <code>Blob</code> value can be read.
* @throws SQLException
* if pos is less than 1 or if pos is greater than the number of bytes
* in the <code>Blob</code> or if pos + length is greater than the number of bytes
* in the <code>Blob</code>
*
* @exception SQLFeatureNotSupportedException
* if the JDBC driver does not support
* this method
* @since 1.6
*/
public synchronized InputStream getBinaryStream(long pos, long length) throws SQLException {
checkClosed();
if (pos < 1) {
throw SQLError.createSQLException("\"pos\" argument can not be < 1.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
pos--;
if (pos > this.binaryData.length) {
throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
this.exceptionInterceptor);
}
if (pos + length > this.binaryData.length) {
throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
this.exceptionInterceptor);
}
return new ByteArrayInputStream(getBinaryData(), (int) pos, (int) length);
}
private synchronized void checkClosed() throws SQLException {
if (this.isClosed) {
throw SQLError.createSQLException("Invalid operation on closed BLOB", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
}
}