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
|
/*-------------------------------------------------------------------------
*
* Copyright (c) 2005-2008, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2BlobClob.java,v 1.9 2008/01/08 06:56:28 jurka Exp $
*
*-------------------------------------------------------------------------
*/
package org.postgresql.jdbc2;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import org.postgresql.core.BaseConnection;
import org.postgresql.largeobject.LargeObject;
import org.postgresql.largeobject.LargeObjectManager;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLState;
import org.postgresql.util.PSQLException;
/**
* This class holds all of the methods common to both Blobs and Clobs.
*
* @author Michael Barker <mailto:mike@middlesoft.co.uk>
*
*/
public abstract class AbstractJdbc2BlobClob
{
protected BaseConnection conn;
protected LargeObject lo;
/**
* We create separate LargeObjects for methods that use streams
* so they won't interfere with each other.
*/
private ArrayList subLOs;
public AbstractJdbc2BlobClob(BaseConnection conn, long oid) throws SQLException
{
this.conn = conn;
LargeObjectManager lom = conn.getLargeObjectAPI();
this.lo = lom.open(oid);
subLOs = new ArrayList();
}
public synchronized void free() throws SQLException
{
if (lo != null) {
lo.close();
lo = null;
}
Iterator i = subLOs.iterator();
while (i.hasNext()) {
LargeObject subLO = (LargeObject)i.next();
subLO.close();
}
subLOs = null;
}
/**
* For Blobs this should be in bytes while for Clobs it should be
* in characters. Since we really haven't figured out how to handle
* character sets for Clobs the current implementation uses bytes for
* both Blobs and Clobs.
*/
public synchronized void truncate(long len) throws SQLException
{
checkFreed();
if (!conn.haveMinimumServerVersion("8.3"))
throw new PSQLException(GT.tr("Truncation of large objects is only implemented in 8.3 and later servers."), PSQLState.NOT_IMPLEMENTED);
assertPosition(len);
lo.truncate((int)len);
}
public synchronized long length() throws SQLException
{
checkFreed();
return lo.size();
}
public synchronized byte[] getBytes(long pos, int length) throws SQLException
{
assertPosition(pos);
lo.seek((int)(pos-1), LargeObject.SEEK_SET);
return lo.read(length);
}
public synchronized InputStream getBinaryStream() throws SQLException
{
checkFreed();
LargeObject subLO = lo.copy();
subLOs.add(subLO);
subLO.seek(0, LargeObject.SEEK_SET);
return subLO.getInputStream();
}
public synchronized OutputStream setBinaryStream(long pos) throws SQLException
{
assertPosition(pos);
LargeObject subLO = lo.copy();
subLOs.add(subLO);
subLO.seek((int)(pos-1));
return subLO.getOutputStream();
}
/**
* Iterate over the buffer looking for the specified pattern
*
* @param pattern A pattern of bytes to search the blob for.
* @param start The position to start reading from.
*/
public synchronized long position(byte[] pattern, long start) throws SQLException
{
assertPosition(start, pattern.length);
int position = 1;
int patternIdx = 0;
long result = -1;
int tmpPosition = 1;
for (LOIterator i = new LOIterator(start-1); i.hasNext(); position++)
{
byte b = i.next();
if (b == pattern[patternIdx])
{
if (patternIdx == 0)
{
tmpPosition = position;
}
patternIdx++;
if (patternIdx == pattern.length)
{
result = tmpPosition;
break;
}
}
else
{
patternIdx = 0;
}
}
return result;
}
/**
* Iterates over a large object returning byte values. Will buffer
* the data from the large object.
*
*
*/
private class LOIterator
{
private static final int BUFFER_SIZE = 8096;
private byte buffer[] = new byte[BUFFER_SIZE];
private int idx = BUFFER_SIZE;
private int numBytes = BUFFER_SIZE;
public LOIterator(long start) throws SQLException
{
lo.seek((int) start);
}
public boolean hasNext() throws SQLException
{
boolean result = false;
if (idx < numBytes)
{
result = true;
}
else
{
numBytes = lo.read(buffer, 0, BUFFER_SIZE);
idx = 0;
result = (numBytes > 0);
}
return result;
}
private byte next()
{
return buffer[idx++];
}
}
/**
* This is simply passing the byte value of the pattern Blob
*/
public synchronized long position(Blob pattern, long start) throws SQLException
{
return position(pattern.getBytes(1, (int)pattern.length()), start);
}
/**
* Throws an exception if the pos value exceeds the max value by
* which the large object API can index.
*
* @param pos Position to write at.
*/
protected void assertPosition(long pos) throws SQLException
{
assertPosition(pos, 0);
}
/**
* Throws an exception if the pos value exceeds the max value by
* which the large object API can index.
*
* @param pos Position to write at.
* @param len number of bytes to write.
*/
protected void assertPosition(long pos, long len) throws SQLException
{
checkFreed();
if (pos < 1)
{
throw new PSQLException(GT.tr("LOB positioning offsets start at 1."), PSQLState.INVALID_PARAMETER_VALUE);
}
if (pos + len - 1 > Integer.MAX_VALUE)
{
throw new PSQLException(GT.tr("PostgreSQL LOBs can only index to: {0}", new Integer(Integer.MAX_VALUE)), PSQLState.INVALID_PARAMETER_VALUE);
}
}
/**
* Checks that this LOB hasn't been free()d already.
* @throws SQLException if LOB has been freed.
*/
protected void checkFreed() throws SQLException
{
if (lo == null)
throw new PSQLException(GT.tr("free() was called on this LOB previously"), PSQLState.OBJECT_NOT_IN_STATE);
}
}
|