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.StringKeyCompare;
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 StringColumn extends Column {
047: private static final Logger log = Logger
048: .getLogger(StringColumn.class.getName());
049: private static final L10N L = new L10N(StringColumn.class);
050:
051: private final int _maxLength;
052:
053: /**
054: * Creates a string column.
055: *
056: * @param columnOffset the offset within the row
057: * @param maxLength the maximum length of the string
058: */
059: StringColumn(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 VARCHAR;
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 2 * _maxLength + 1;
097: }
098:
099: /**
100: * Returns the key compare for the column.
101: */
102: public KeyCompare getIndexKeyCompare() {
103: return new StringKeyCompare();
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 + 2 * _maxLength + 1;
124:
125: block[offset++] = (byte) (len);
126: for (int i = 0; i < len && offset < maxOffset; i++) {
127: int ch = str.charAt(i);
128:
129: block[offset++] = (byte) (ch >> 8);
130: block[offset++] = (byte) (ch);
131: }
132:
133: setNonNull(block, rowOffset);
134: }
135:
136: public String getString(byte[] block, int rowOffset) {
137: if (isNull(block, rowOffset))
138: return null;
139:
140: int startOffset = rowOffset + _columnOffset;
141: int len = block[startOffset] & 0xff;
142:
143: char[] cBuf = new char[len];
144:
145: int offset = startOffset + 1;
146: int endOffset = offset + 2 * len;
147: int i = 0;
148: while (offset < endOffset) {
149: int ch1 = block[offset++] & 0xff;
150: int ch2 = block[offset++] & 0xff;
151:
152: cBuf[i++] = (char) ((ch1 << 8) + ch2);
153: }
154:
155: return new String(cBuf, 0, cBuf.length);
156: }
157:
158: /**
159: * Sets the column based on an expression.
160: *
161: * @param block the block's buffer
162: * @param rowOffset the offset of the row in the block
163: * @param expr the expression to store
164: */
165: void setExpr(Transaction xa, byte[] block, int rowOffset,
166: Expr expr, QueryContext context) throws SQLException {
167: if (expr.isNull(null))
168: setNull(block, rowOffset);
169: else
170: setString(xa, block, rowOffset, expr.evalString(context));
171: }
172:
173: /**
174: * Returns true if the items in the given rows match.
175: */
176: public boolean isEqual(byte[] block1, int rowOffset1,
177: byte[] block2, int rowOffset2) {
178: if (isNull(block1, rowOffset1) != isNull(block2, rowOffset2))
179: return false;
180:
181: int startOffset1 = rowOffset1 + _columnOffset;
182: int len1 = block1[startOffset1] & 0xff;
183:
184: int startOffset2 = rowOffset2 + _columnOffset;
185: int len2 = block2[startOffset2] & 0xff;
186:
187: if (len1 != len2)
188: return false;
189:
190: for (int i = 2 * len1; i > 0; i--) {
191: if (block1[startOffset1 + i] != block2[startOffset2 + i])
192: return false;
193: }
194:
195: return true;
196: }
197:
198: /**
199: * Returns true if the bytes match.
200: */
201: public boolean isEqual(byte[] block, int rowOffset, byte[] buffer,
202: int offset, int length) {
203: if (isNull(block, rowOffset))
204: return false;
205:
206: int startOffset = rowOffset + _columnOffset;
207: int len = block[startOffset] & 0xff;
208:
209: if (len != length)
210: return false;
211:
212: int blockOffset = startOffset + 1;
213: int endOffset = blockOffset + 2 * len;
214: while (blockOffset < endOffset) {
215: if (block[blockOffset++] != buffer[offset++])
216: return false;
217: }
218:
219: return true;
220: }
221:
222: public boolean isEqual(byte[] block, int rowOffset, String value) {
223: if (value == null)
224: return isNull(block, rowOffset);
225: else if (isNull(block, rowOffset))
226: return false;
227:
228: int startOffset = rowOffset + _columnOffset;
229: int len = block[startOffset] & 0xff;
230:
231: int strLength = value.length();
232: int strOffset = 0;
233:
234: int offset = startOffset + 1;
235: int endOffset = offset + 2 * len;
236: while (offset < endOffset && strOffset < strLength) {
237: int ch1 = ((block[offset++] & 0xff) << 8)
238: + (block[offset++] & 0xff);
239: char ch = value.charAt(strOffset++);
240:
241: if (ch1 != ch)
242: return false;
243: }
244:
245: return offset == endOffset && strOffset == strLength;
246: }
247:
248: /**
249: * Evaluates the column to a stream.
250: */
251: public void evalToResult(byte[] block, int rowOffset,
252: SelectResult result) {
253: if (isNull(block, rowOffset)) {
254: result.writeNull();
255: return;
256: }
257:
258: int startOffset = rowOffset + _columnOffset;
259: int len = block[startOffset] & 0xff;
260:
261: result.writeString(block, startOffset + 1, len);
262: /*
263: result.write(Column.VARCHAR);
264: result.write(0);
265: result.write(0);
266: result.write(0);
267: result.write(len);
268: result.write(block, startOffset + 1, len);
269: */
270: }
271:
272: /**
273: * Evaluate to a buffer.
274: *
275: * @param block the block's buffer
276: * @param rowOffset the offset of the row in the block
277: * @param buffer the result buffer
278: * @param buffer the result buffer offset
279: *
280: * @return the length of the value
281: */
282: int evalToBuffer(byte[] block, int rowOffset, byte[] buffer,
283: int bufferOffset) throws SQLException {
284: if (isNull(block, rowOffset))
285: return 0;
286:
287: int startOffset = rowOffset + _columnOffset;
288: // static length for now
289: int len = getLength();
290:
291: System.arraycopy(block, startOffset, buffer, bufferOffset, len);
292:
293: return len;
294: }
295:
296: /**
297: * Sets any index for the column.
298: *
299: * @param block the block's buffer
300: * @param rowOffset the offset of the row in the block
301: * @param rowAddr the address of the row
302: */
303: void setIndex(Transaction xa, byte[] block, int rowOffset,
304: long rowAddr, QueryContext context) throws SQLException {
305: BTree index = getIndex();
306:
307: if (index != null) {
308: try {
309: index.insert(block, rowOffset + _columnOffset,
310: getLength(), rowAddr, xa, false);
311: } catch (SQLException e) {
312: log.log(Level.FINER, e.toString(), e);
313:
314: throw new SQLExceptionWrapper(
315: L
316: .l(
317: "StringColumn '{0}.{1}' unique index set failed for {2}.\n{3}",
318: getTable().getName(),
319: getName(), getString(block,
320: rowOffset), e
321: .toString()), e);
322: }
323: }
324: }
325:
326: /**
327: * Deleting the row, based on the column.
328: *
329: * @param block the block's buffer
330: * @param rowOffset the offset of the row in the block
331: * @param expr the expression to store
332: */
333: void delete(Transaction xa, byte[] block, int rowOffset)
334: throws SQLException {
335: BTree index = getIndex();
336:
337: if (index != null)
338: index.remove(block, rowOffset + _columnOffset, getLength(),
339: xa);
340: }
341:
342: public String toString() {
343: if (getIndex() != null)
344: return "StringColumn[" + getName() + ",index]";
345: else
346: return "StringColumn[" + getName() + "]";
347: }
348: }
|