001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.db.table;
031:
032: import com.caucho.db.sql.Expr;
033: import com.caucho.db.sql.QueryContext;
034: import com.caucho.db.sql.SelectResult;
035: import com.caucho.db.store.BlobInputStream;
036: import com.caucho.db.store.BlobOutputStream;
037: import com.caucho.db.store.Inode;
038: import com.caucho.db.store.Transaction;
039:
040: import java.io.IOException;
041: import java.io.InputStream;
042: import java.sql.SQLException;
043: import java.util.logging.Level;
044:
045: class BlobColumn extends Column {
046: /**
047: * Creates an inode column.
048: *
049: * @param columnOffset the offset within the row
050: * @param maxLength the maximum length of the string
051: */
052: BlobColumn(Row row, String name) {
053: super (row, name);
054: }
055:
056: /**
057: * Returns the type code for the column.
058: */
059: public int getTypeCode() {
060: return BLOB;
061: }
062:
063: /**
064: * Returns the java type.
065: */
066: public Class getJavaType() {
067: return java.sql.Blob.class;
068: }
069:
070: /**
071: * Returns the declaration size
072: */
073: public int getDeclarationSize() {
074: return 128;
075: }
076:
077: /**
078: * Returns the column's size.
079: */
080: public int getLength() {
081: return 128;
082: }
083:
084: /**
085: * Sets the string value.
086: *
087: * @param block the buffer to store the row
088: * @param rowOffset the offset into the row
089: * @param str the string value
090: */
091: void setString(Transaction xa, byte[] block, int rowOffset,
092: String str) {
093: if (!isNull(block, rowOffset)) {
094: long length = Inode.readLong(block, rowOffset
095: + _columnOffset);
096:
097: if (Table.INLINE_BLOB_SIZE <= length) {
098: Inode inode = new Inode();
099: inode.init(getTable(), xa, block, rowOffset
100: + _columnOffset);
101: xa.addDeleteInode(inode);
102: }
103: }
104:
105: if (str == null) {
106: setNull(block, rowOffset);
107: return;
108: }
109:
110: setNonNull(block, rowOffset);
111:
112: try {
113: BlobOutputStream os;
114: os = new BlobOutputStream(xa, getTable(), block, rowOffset
115: + _columnOffset);
116:
117: int length = str.length();
118: for (int i = 0; i < length; i++) {
119: int ch = str.charAt(i);
120:
121: if (ch < 0x80)
122: os.write(ch);
123: else if (ch < 0x800) {
124: os.write(0xc0 + (ch >> 6));
125: os.write(0x80 + (ch & 0x3f));
126: } else {
127: os.write(0xe0 + (ch >> 12));
128: os.write(0x80 + ((ch >> 6) & 0x3f));
129: os.write(0x80 + (ch & 0x3f));
130: }
131: }
132:
133: os.close();
134: } catch (IOException e) {
135: log.log(Level.WARNING, e.toString(), e);
136: }
137: }
138:
139: /**
140: * Sets the string value.
141: *
142: * @param block the buffer to store the row
143: * @param rowOffset the offset into the row
144: * @param str the string value
145: */
146: private void setStream(Transaction xa, byte[] block, int rowOffset,
147: InputStream value) {
148: if (!isNull(block, rowOffset)) {
149: long length = Inode.readLong(block, rowOffset
150: + _columnOffset);
151:
152: if (Table.INLINE_BLOB_SIZE <= length) {
153: Inode inode = new Inode();
154: inode.init(getTable(), xa, block, rowOffset
155: + _columnOffset);
156: xa.addDeleteInode(inode);
157: }
158: }
159:
160: if (value == null) {
161: setNull(block, rowOffset);
162: return;
163: }
164:
165: setNonNull(block, rowOffset);
166:
167: try {
168: BlobOutputStream os;
169: os = new BlobOutputStream(xa, getTable(), block, rowOffset
170: + _columnOffset);
171:
172: int data;
173: while ((data = value.read()) >= 0) {
174: os.write(data);
175: }
176:
177: os.close();
178: value.close();
179: } catch (IOException e) {
180: log.log(Level.WARNING, e.toString(), e);
181: }
182: }
183:
184: /**
185: * Deleting the row, based on the column.
186: *
187: * @param block the block's buffer
188: * @param rowOffset the offset of the row in the block
189: * @param expr the expression to store
190: */
191: @Override
192: void delete(Transaction xa, byte[] block, int rowOffset)
193: throws SQLException {
194: if (!isNull(block, rowOffset)) {
195: long length = Inode.readLong(block, rowOffset
196: + _columnOffset);
197:
198: if (length < Table.INLINE_BLOB_SIZE)
199: return;
200:
201: Inode inode = new Inode();
202: inode
203: .init(getTable(), xa, block, rowOffset
204: + _columnOffset);
205: xa.addDeleteInode(inode);
206: }
207: }
208:
209: @Override
210: public String getString(byte[] block, int rowOffset) {
211: if (isNull(block, rowOffset))
212: return null;
213:
214: try {
215: BlobInputStream is;
216: is = new BlobInputStream(getTable(), block, rowOffset
217: + _columnOffset);
218:
219: int ch;
220: StringBuilder cb = new StringBuilder();
221:
222: while ((ch = is.read()) >= 0) {
223: if (ch < 0x80)
224: cb.append((char) ch);
225: else if ((ch & 0xe0) == 0xc0) {
226: int ch1 = is.read();
227:
228: cb
229: .append((char) (((ch & 0x3f) << 6) + (ch1 & 0x3f)));
230: } else {
231: int ch1 = is.read();
232: int ch2 = is.read();
233:
234: cb.append((char) (((ch & 0xf) << 12)
235: + ((ch1 & 0x3f) << 6) + ((ch2 & 0x3f))));
236: }
237: }
238:
239: is.close();
240:
241: return cb.toString();
242: } catch (IOException e) {
243: log.log(Level.WARNING, e.toString(), e);
244: }
245:
246: return null;
247: }
248:
249: /**
250: * Sets based on an iterator.
251: */
252: public void set(Transaction xa, TableIterator iter, Expr expr,
253: QueryContext context) throws SQLException {
254: byte[] block = iter.getBuffer();
255: int rowOffset = iter.getRowOffset();
256:
257: if (expr.isNull(null))
258: setNull(block, rowOffset);
259: else if (expr.isBinaryStream())
260: setStream(xa, block, rowOffset, expr.evalStream(context));
261: else
262: setString(xa, block, rowOffset, expr.evalString(context));
263:
264: iter.setDirty();
265: }
266:
267: /**
268: * Sets the column based on an expression.
269: *
270: * @param block the block's buffer
271: * @param rowOffset the offset of the row in the block
272: * @param expr the expression to store
273: */
274: void setExpr(Transaction xa, byte[] block, int rowOffset,
275: Expr expr, QueryContext context) throws SQLException {
276: if (expr.isNull(null)) {
277: setNull(block, rowOffset);
278: } else if (expr.isBinaryStream()) {
279: setStream(xa, block, rowOffset, expr.evalStream(context));
280: } else {
281: setString(xa, block, rowOffset, expr.evalString(context));
282: }
283: }
284:
285: /**
286: * Returns true if the items in the given rows match.
287: */
288: public boolean isEqual(byte[] block1, int rowOffset1,
289: byte[] block2, int rowOffset2) {
290: if (isNull(block1, rowOffset1) != isNull(block2, rowOffset2))
291: return false;
292:
293: int startOffset1 = rowOffset1 + _columnOffset;
294:
295: int startOffset2 = rowOffset2 + _columnOffset;
296:
297: for (int i = 128 - 1; i >= 0; i--) {
298: if (block1[startOffset1 + i] != block2[startOffset2 + i])
299: return false;
300: }
301:
302: return true;
303: }
304:
305: /**
306: * Returns true if the bytes match.
307: */
308: public boolean isEqual(byte[] block, int rowOffset, byte[] buffer,
309: int offset, int length) {
310: if (isNull(block, rowOffset))
311: return false;
312:
313: return false;
314: }
315:
316: public boolean isEqual(byte[] block, int rowOffset, String value) {
317: if (value == null)
318: return isNull(block, rowOffset);
319: else if (isNull(block, rowOffset))
320: return false;
321:
322: return false;
323: }
324:
325: /**
326: * Evaluates the column to a stream.
327: */
328: @Override
329: public void evalToResult(byte[] block, int rowOffset,
330: SelectResult result) {
331: if (isNull(block, rowOffset)) {
332: result.writeNull();
333: return;
334: }
335:
336: result.writeBlob(block, rowOffset + _columnOffset);
337: }
338:
339: public String toString() {
340: return "BlobColumn[" + getName() + "]";
341: }
342: }
|