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.index.BTree;
033: import com.caucho.db.index.KeyCompare;
034: import com.caucho.db.index.BinaryKeyCompare;
035: import com.caucho.db.sql.Expr;
036: import com.caucho.db.sql.QueryContext;
037: import com.caucho.db.sql.SelectResult;
038: import com.caucho.db.store.Transaction;
039: import com.caucho.sql.SQLExceptionWrapper;
040: import com.caucho.util.L10N;
041:
042: import java.sql.SQLException;
043: import java.util.logging.Level;
044: import java.util.logging.Logger;
045:
046: class BinaryColumn extends Column {
047: private static final Logger log = Logger
048: .getLogger(BinaryColumn.class.getName());
049: private static final L10N L = new L10N(BinaryColumn.class);
050:
051: private final int _maxLength;
052:
053: /**
054: * Creates a binary column.
055: *
056: * @param columnOffset the offset within the row
057: * @param maxLength the maximum length of the binary
058: */
059: BinaryColumn(Row row, String name, int maxLength) {
060: super (row, name);
061:
062: if (maxLength < 0)
063: throw new IllegalArgumentException(
064: "length must be non-negative");
065: else if (255 < maxLength)
066: throw new IllegalArgumentException("length too big");
067:
068: _maxLength = maxLength;
069: }
070:
071: /**
072: * Returns the type code for the column.
073: */
074: public int getTypeCode() {
075: return VARBINARY;
076: }
077:
078: /**
079: * Returns the java type.
080: */
081: public Class getJavaType() {
082: return String.class;
083: }
084:
085: /**
086: * Returns the declaration size
087: */
088: public int getDeclarationSize() {
089: return _maxLength;
090: }
091:
092: /**
093: * Returns the column's size.
094: */
095: public int getLength() {
096: return _maxLength + 1;
097: }
098:
099: /**
100: * Returns the key compare for the column.
101: */
102: public KeyCompare getIndexKeyCompare() {
103: return new BinaryKeyCompare();
104: }
105:
106: /**
107: * Sets the string value.
108: *
109: * @param block the buffer to store the row
110: * @param rowOffset the offset into the row
111: * @param str the string value
112: */
113: void setString(Transaction xa, byte[] block, int rowOffset,
114: String str) {
115: int offset = rowOffset + _columnOffset;
116:
117: if (str == null) {
118: setNull(block, rowOffset);
119: return;
120: }
121:
122: int len = str.length();
123: int maxOffset = offset + _maxLength + 1;
124:
125: int lenOffset = offset++;
126: for (int i = 0; i < len && offset < maxOffset; i++) {
127: int ch = str.charAt(i);
128:
129: if (ch < 0x80)
130: block[offset++] = (byte) ch;
131: else if (ch < 0x800) {
132: block[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
133: block[offset++] = (byte) (0x80 + (ch & 0x3f));
134: } else {
135: block[offset++] = (byte) (0xe0 + ((ch >> 12) & 0x0f));
136: block[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
137: block[offset++] = (byte) (0x80 + (ch & 0x3f));
138: }
139: }
140: block[lenOffset] = (byte) (offset - lenOffset - 1);
141:
142: setNonNull(block, rowOffset);
143: }
144:
145: public String getString(byte[] block, int rowOffset) {
146: if (isNull(block, rowOffset))
147: return null;
148:
149: int startOffset = rowOffset + _columnOffset;
150: int len = block[startOffset] & 0xff;
151:
152: StringBuffer sb = new StringBuffer();
153:
154: int offset = startOffset + 1;
155: int endOffset = offset + len;
156: int i = 0;
157: while (offset < endOffset) {
158: int ch1 = block[offset++] & 0xff;
159:
160: if (ch1 < 0x80)
161: sb.append((char) ch1);
162: else if ((ch1 & 0xe0) == 0xc0) {
163: int ch2 = block[offset++] & 0xff;
164:
165: sb.append((char) ((ch1 & 0x1f) << 6) + (ch2 & 0x3f));
166: } else {
167: int ch2 = block[offset++] & 0xff;
168: int ch3 = block[offset++] & 0xff;
169:
170: sb.append((char) ((ch1 & 0x0f) << 12)
171: + ((ch2 & 0x3f) << 6) + ((ch3 & 0x3f)));
172: }
173: }
174:
175: return sb.toString();
176: }
177:
178: /**
179: * Sets the column based on an expression.
180: *
181: * @param block the block's buffer
182: * @param rowOffset the offset of the row in the block
183: * @param expr the expression to store
184: */
185: void setExpr(Transaction xa, byte[] block, int rowOffset,
186: Expr expr, QueryContext context) throws SQLException {
187: if (expr.isNull(null))
188: setNull(block, rowOffset);
189: else
190: setString(xa, block, rowOffset, expr.evalString(context));
191: }
192:
193: /**
194: * Returns true if the items in the given rows match.
195: */
196: public boolean isEqual(byte[] block1, int rowOffset1,
197: byte[] block2, int rowOffset2) {
198: if (isNull(block1, rowOffset1) != isNull(block2, rowOffset2))
199: return false;
200:
201: int startOffset1 = rowOffset1 + _columnOffset;
202: int len1 = block1[startOffset1] & 0xff;
203:
204: int startOffset2 = rowOffset2 + _columnOffset;
205: int len2 = block2[startOffset2] & 0xff;
206:
207: if (len1 != len2)
208: return false;
209:
210: for (int i = len1; i > 0; i--) {
211: if (block1[startOffset1 + i] != block2[startOffset2 + i])
212: return false;
213: }
214:
215: return true;
216: }
217:
218: /**
219: * Returns true if the bytes match.
220: */
221: public boolean isEqual(byte[] block, int rowOffset, byte[] buffer,
222: int offset, int length) {
223: if (isNull(block, rowOffset))
224: return false;
225:
226: int startOffset = rowOffset + _columnOffset;
227: int len = block[startOffset] & 0xff;
228:
229: if (len != length)
230: return false;
231:
232: int blockOffset = startOffset + 1;
233: int endOffset = blockOffset + len;
234: while (blockOffset < endOffset) {
235: if (block[blockOffset++] != buffer[offset++])
236: return false;
237: }
238:
239: return true;
240: }
241:
242: public boolean isEqual(byte[] block, int rowOffset, String value) {
243: if (value == null)
244: return isNull(block, rowOffset);
245: else if (isNull(block, rowOffset))
246: return false;
247:
248: int startOffset = rowOffset + _columnOffset;
249: int len = block[startOffset] & 0xff;
250:
251: int strLength = value.length();
252: int strOffset = 0;
253:
254: int offset = startOffset + 1;
255: int endOffset = offset + len;
256: while (offset < endOffset && strOffset < strLength) {
257: char ch = value.charAt(strOffset++);
258:
259: int ch1 = block[offset++] & 0xff;
260:
261: // XXX: missing utf-8
262: if (ch1 != ch)
263: return false;
264: }
265:
266: return offset == endOffset && strOffset == strLength;
267: }
268:
269: /**
270: * Evaluates the column to a stream.
271: */
272: public void evalToResult(byte[] block, int rowOffset,
273: SelectResult result) {
274: if (isNull(block, rowOffset)) {
275: result.writeNull();
276: return;
277: }
278:
279: // XXX: add writeVarBinary to SelectResult
280: result.writeString(getString(block, rowOffset));
281: }
282:
283: /**
284: * Evaluate to a buffer.
285: *
286: * @param block the block's buffer
287: * @param rowOffset the offset of the row in the block
288: * @param buffer the result buffer
289: * @param buffer the result buffer offset
290: *
291: * @return the length of the value
292: */
293: int evalToBuffer(byte[] block, int rowOffset, byte[] buffer,
294: int bufferOffset) throws SQLException {
295: if (isNull(block, rowOffset))
296: return 0;
297:
298: int startOffset = rowOffset + _columnOffset;
299: // static length for now
300: int len = getLength();
301:
302: System.arraycopy(block, startOffset, buffer, bufferOffset, len);
303:
304: return len;
305: }
306:
307: /**
308: * Sets any index for the column.
309: *
310: * @param block the block's buffer
311: * @param rowOffset the offset of the row in the block
312: * @param rowAddr the address of the row
313: */
314: void setIndex(Transaction xa, byte[] block, int rowOffset,
315: long rowAddr, QueryContext context) throws SQLException {
316: BTree index = getIndex();
317:
318: if (index != null) {
319: try {
320: index.insert(block, rowOffset + _columnOffset,
321: getLength(), rowAddr, xa, false);
322: } catch (SQLException e) {
323: log.log(Level.FINER, e.toString(), e);
324:
325: throw new SQLExceptionWrapper(
326: L
327: .l(
328: "StringColumn '{0}.{1}' unique index set failed for {2}\n{3}",
329: getTable().getName(),
330: getName(), getString(block,
331: rowOffset), e
332: .toString()), e);
333: }
334: }
335: }
336:
337: /**
338: * Deleting the row, based on the column.
339: *
340: * @param block the block's buffer
341: * @param rowOffset the offset of the row in the block
342: * @param expr the expression to store
343: */
344: void delete(Transaction xa, byte[] block, int rowOffset)
345: throws SQLException {
346: BTree index = getIndex();
347:
348: if (index != null)
349: index.remove(block, rowOffset + _columnOffset, getLength(),
350: xa);
351: }
352:
353: public String toString() {
354: if (getIndex() != null)
355: return "VarBinaryColumn[" + getName() + ",index]";
356: else
357: return "VarBinaryColumn[" + getName() + "]";
358: }
359: }
|