001: /*-------------------------------------------------------------------------
002: *
003: * Copyright (c) 2005, PostgreSQL Global Development Group
004: *
005: * IDENTIFICATION
006: * $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2BlobClob.java,v 1.7 2007/03/29 06:13:53 jurka Exp $
007: *
008: *-------------------------------------------------------------------------
009: */
010: package org.postgresql.jdbc2;
011:
012: import java.io.InputStream;
013: import java.io.OutputStream;
014: import java.sql.Blob;
015: import java.sql.SQLException;
016: import java.util.ArrayList;
017: import java.util.Iterator;
018:
019: import org.postgresql.core.BaseConnection;
020: import org.postgresql.largeobject.LargeObject;
021: import org.postgresql.largeobject.LargeObjectManager;
022: import org.postgresql.util.GT;
023: import org.postgresql.util.PSQLState;
024: import org.postgresql.util.PSQLException;
025:
026: /**
027: * This class holds all of the methods common to both Blobs and Clobs.
028: *
029: * @author Michael Barker <mailto:mike@middlesoft.co.uk>
030: *
031: */
032: public class AbstractJdbc2BlobClob {
033: protected BaseConnection conn;
034: protected LargeObject lo;
035:
036: /**
037: * We create separate LargeObjects for methods that use streams
038: * so they won't interfere with each other.
039: */
040: private ArrayList subLOs;
041:
042: public AbstractJdbc2BlobClob(BaseConnection conn, long oid)
043: throws SQLException {
044: this .conn = conn;
045: LargeObjectManager lom = conn.getLargeObjectAPI();
046: this .lo = lom.open(oid);
047: subLOs = new ArrayList();
048: }
049:
050: public synchronized void free() throws SQLException {
051: if (lo != null) {
052: lo.close();
053: lo = null;
054: }
055: Iterator i = subLOs.iterator();
056: while (i.hasNext()) {
057: LargeObject subLO = (LargeObject) i.next();
058: subLO.close();
059: }
060: subLOs = null;
061: }
062:
063: /**
064: * For Blobs this should be in bytes while for Clobs it should be
065: * in characters. Since we really haven't figured out how to handle
066: * character sets for Clobs the current implementation uses bytes for
067: * both Blobs and Clobs.
068: */
069: public synchronized void truncate(long len) throws SQLException {
070: checkFreed();
071: if (!conn.haveMinimumServerVersion("8.3"))
072: throw new PSQLException(
073: GT
074: .tr("Truncation of large objects is only implemented in 8.3 and later servers."),
075: PSQLState.NOT_IMPLEMENTED);
076:
077: assertPosition(len);
078: lo.truncate((int) len);
079: }
080:
081: public synchronized long length() throws SQLException {
082: checkFreed();
083: return lo.size();
084: }
085:
086: public synchronized byte[] getBytes(long pos, int length)
087: throws SQLException {
088: assertPosition(pos);
089: lo.seek((int) (pos - 1), LargeObject.SEEK_SET);
090: return lo.read(length);
091: }
092:
093: public synchronized InputStream getBinaryStream()
094: throws SQLException {
095: checkFreed();
096: LargeObject subLO = lo.copy();
097: subLOs.add(subLO);
098: subLO.seek(0, LargeObject.SEEK_SET);
099: return subLO.getInputStream();
100: }
101:
102: public synchronized OutputStream setBinaryStream(long pos)
103: throws SQLException {
104: assertPosition(pos);
105: LargeObject subLO = lo.copy();
106: subLOs.add(subLO);
107: subLO.seek((int) (pos - 1));
108: return subLO.getOutputStream();
109: }
110:
111: /**
112: * Iterate over the buffer looking for the specified pattern
113: *
114: * @param pattern A pattern of bytes to search the blob for.
115: * @param start The position to start reading from.
116: */
117: public synchronized long position(byte[] pattern, long start)
118: throws SQLException {
119: assertPosition(start, pattern.length);
120:
121: int position = 1;
122: int patternIdx = 0;
123: long result = -1;
124: int tmpPosition = 1;
125:
126: for (LOIterator i = new LOIterator(start - 1); i.hasNext(); position++) {
127: byte b = i.next();
128: if (b == pattern[patternIdx]) {
129: if (patternIdx == 0) {
130: tmpPosition = position;
131: }
132: patternIdx++;
133: if (patternIdx == pattern.length) {
134: result = tmpPosition;
135: break;
136: }
137: } else {
138: patternIdx = 0;
139: }
140: }
141:
142: return result;
143: }
144:
145: /**
146: * Iterates over a large object returning byte values. Will buffer
147: * the data from the large object.
148: *
149: *
150: */
151: private class LOIterator {
152: private static final int BUFFER_SIZE = 8096;
153: private byte buffer[] = new byte[BUFFER_SIZE];
154: private int idx = BUFFER_SIZE;
155: private int numBytes = BUFFER_SIZE;
156:
157: public LOIterator(long start) throws SQLException {
158: lo.seek((int) start);
159: }
160:
161: public boolean hasNext() throws SQLException {
162: boolean result = false;
163: if (idx < numBytes) {
164: result = true;
165: } else {
166: numBytes = lo.read(buffer, 0, BUFFER_SIZE);
167: idx = 0;
168: result = (numBytes > 0);
169: }
170: return result;
171: }
172:
173: private byte next() {
174: return buffer[idx++];
175: }
176: }
177:
178: /**
179: * This is simply passing the byte value of the pattern Blob
180: */
181: public synchronized long position(Blob pattern, long start)
182: throws SQLException {
183: return position(pattern.getBytes(1, (int) pattern.length()),
184: start);
185: }
186:
187: /**
188: * Throws an exception if the pos value exceeds the max value by
189: * which the large object API can index.
190: *
191: * @param pos Position to write at.
192: */
193: protected void assertPosition(long pos) throws SQLException {
194: assertPosition(pos, 0);
195: }
196:
197: /**
198: * Throws an exception if the pos value exceeds the max value by
199: * which the large object API can index.
200: *
201: * @param pos Position to write at.
202: * @param len number of bytes to write.
203: */
204: protected void assertPosition(long pos, long len)
205: throws SQLException {
206: checkFreed();
207: if (pos < 1) {
208: throw new PSQLException(GT
209: .tr("LOB positioning offsets start at 1."),
210: PSQLState.INVALID_PARAMETER_VALUE);
211: }
212: if (pos + len - 1 > Integer.MAX_VALUE) {
213: throw new PSQLException(GT.tr(
214: "PostgreSQL LOBs can only index to: {0}",
215: new Integer(Integer.MAX_VALUE)),
216: PSQLState.INVALID_PARAMETER_VALUE);
217: }
218: }
219:
220: /**
221: * Checks that this LOB hasn't been free()d already.
222: * @throws SQLException if LOB has been freed.
223: */
224: protected void checkFreed() throws SQLException {
225: if (lo == null)
226: throw new PSQLException(GT
227: .tr("free() was called on this LOB previously"),
228: PSQLState.OBJECT_NOT_IN_STATE);
229: }
230:
231: }
|