001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.value;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.InputStream;
010: import java.io.Reader;
011: import java.lang.ref.SoftReference;
012: import java.math.BigDecimal;
013: import java.sql.Date;
014: import java.sql.PreparedStatement;
015: import java.sql.SQLException;
016: import java.sql.Time;
017: import java.sql.Timestamp;
018: import java.sql.Types;
019:
020: import org.h2.constant.ErrorCode;
021: import org.h2.constant.SysProperties;
022: import org.h2.engine.Constants;
023: import org.h2.message.Message;
024: import org.h2.store.DataHandler;
025: import org.h2.tools.SimpleResultSet;
026: import org.h2.util.ByteUtils;
027: import org.h2.util.IOUtils;
028: import org.h2.util.MathUtils;
029: import org.h2.util.StringUtils;
030:
031: /**
032: * This is the base class for all value classes.
033: * It provides conversion and comparison methods.
034: */
035: public abstract class Value {
036:
037: /**
038: * The data type is unknown at this time.
039: */
040: public static final int UNKNOWN = -1;
041: public static final int NULL = 0, BOOLEAN = 1, BYTE = 2, SHORT = 3,
042: INT = 4, LONG = 5, DECIMAL = 6;
043: public static final int DOUBLE = 7, FLOAT = 8, TIME = 9, DATE = 10,
044: TIMESTAMP = 11, BYTES = 12;
045: public static final int STRING = 13, STRING_IGNORECASE = 14,
046: BLOB = 15, CLOB = 16;
047: public static final int ARRAY = 17, RESULT_SET = 18,
048: JAVA_OBJECT = 19, UUID = 20, STRING_FIXED = 21;
049:
050: public static final int TYPE_COUNT = STRING_FIXED + 1;
051:
052: private static SoftReference softCache = new SoftReference(null);
053: private static final BigDecimal MAX_LONG_DECIMAL = new BigDecimal(
054: "" + Long.MAX_VALUE);
055: private static final BigDecimal MIN_LONG_DECIMAL = new BigDecimal(
056: "" + Long.MIN_VALUE);
057:
058: /**
059: * Get the SQL expression for this value.
060: *
061: * @return the SQL expression
062: */
063: public abstract String getSQL();
064:
065: /**
066: * Get the value type.
067: *
068: * @return the type
069: */
070: public abstract int getType();
071:
072: /**
073: * Get the precision.
074: *
075: * @return the precision
076: */
077: public abstract long getPrecision();
078:
079: /**
080: * Get the display size in characters.
081: *
082: * @return the display size
083: */
084: public abstract int getDisplaySize();
085:
086: /**
087: * Get the memory used by this object.
088: *
089: * @return the memory used in bytes
090: */
091: public int getMemory() {
092: return DataType.getDataType(getType()).memory * 4;
093: }
094:
095: /**
096: * Get the value as a string.
097: *
098: * @return the string
099: */
100: public abstract String getString();
101:
102: /**
103: * Get the value as an object.
104: *
105: * @return the object
106: */
107: public abstract Object getObject();
108:
109: /**
110: * Set the value as a parameter in a prepared statement.
111: *
112: * @param prep the prepared statement
113: * @param parameterIndex the parameter index
114: */
115: public abstract void set(PreparedStatement prep, int parameterIndex)
116: throws SQLException;
117:
118: /**
119: * Compare the value with another value of the same type.
120: *
121: * @param v the other value
122: * @param mode the compare mode
123: * @return 0 if both values are equal, -1 if the other value is smaller, and
124: * 1 otherwise
125: */
126: protected abstract int compareSecure(Value v, CompareMode mode)
127: throws SQLException;
128:
129: public abstract int hashCode();
130:
131: /**
132: * Check if the two values are equal.
133: * No data conversion is made; this method returns false
134: * if the other object is not of the same class.
135: *
136: * @param other the other value
137: * @return true if they are equal
138: */
139: public abstract boolean equals(Object other);
140:
141: public static int getOrder(int type) {
142: switch (type) {
143: case UNKNOWN:
144: return 1;
145: case NULL:
146: return 2;
147: case STRING:
148: return 10;
149: case CLOB:
150: return 11;
151: case STRING_FIXED:
152: return 12;
153: case STRING_IGNORECASE:
154: return 13;
155: case BOOLEAN:
156: return 20;
157: case BYTE:
158: return 21;
159: case SHORT:
160: return 22;
161: case INT:
162: return 23;
163: case LONG:
164: return 24;
165: case DECIMAL:
166: return 25;
167: case FLOAT:
168: return 26;
169: case DOUBLE:
170: return 27;
171: case TIME:
172: return 30;
173: case DATE:
174: return 31;
175: case TIMESTAMP:
176: return 32;
177: case BYTES:
178: return 40;
179: case BLOB:
180: return 41;
181: case UUID:
182: return 42;
183: case JAVA_OBJECT:
184: return 43;
185: case ARRAY:
186: return 50;
187: case RESULT_SET:
188: return 51;
189: default:
190: throw Message.getInternalError("type:" + type);
191: }
192: }
193:
194: public static int getHigherOrder(int t1, int t2)
195: throws SQLException {
196: if (t1 == t2) {
197: if (t1 == Value.UNKNOWN) {
198: throw Message.getSQLException(
199: ErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
200: }
201: return t1;
202: }
203: int o1 = getOrder(t1);
204: int o2 = getOrder(t2);
205: return o1 > o2 ? t1 : t2;
206: }
207:
208: static Value cache(Value v) {
209: if (SysProperties.OBJECT_CACHE) {
210: Value[] cache = (Value[]) softCache.get();
211: int hash = v.hashCode();
212: if (cache == null) {
213: cache = new Value[SysProperties.OBJECT_CACHE_SIZE];
214: softCache = new SoftReference(cache);
215: }
216: int index = hash & (SysProperties.OBJECT_CACHE_SIZE - 1);
217: Value cached = cache[index];
218: if (cached != null) {
219: if (cached.getType() == v.getType() && v.equals(cached)) {
220: // cacheHit++;
221: return cached;
222: }
223: }
224: // cacheMiss++;
225: // cache[cacheCleaner] = null;
226: // cacheCleaner = (cacheCleaner + 1) & (Constants.OBJECT_CACHE_SIZE - 1);
227: cache[index] = v;
228: }
229: return v;
230: }
231:
232: public Boolean getBoolean() throws SQLException {
233: return ((ValueBoolean) convertTo(Value.BOOLEAN)).getBoolean();
234: }
235:
236: public Date getDate() throws SQLException {
237: return ((ValueDate) convertTo(Value.DATE)).getDate();
238: }
239:
240: public Date getDateNoCopy() throws SQLException {
241: return ((ValueDate) convertTo(Value.DATE)).getDateNoCopy();
242: }
243:
244: public Time getTime() throws SQLException {
245: return ((ValueTime) convertTo(Value.TIME)).getTime();
246: }
247:
248: public Time getTimeNoCopy() throws SQLException {
249: return ((ValueTime) convertTo(Value.TIME)).getTimeNoCopy();
250: }
251:
252: public Timestamp getTimestamp() throws SQLException {
253: return ((ValueTimestamp) convertTo(Value.TIMESTAMP))
254: .getTimestamp();
255: }
256:
257: public Timestamp getTimestampNoCopy() throws SQLException {
258: return ((ValueTimestamp) convertTo(Value.TIMESTAMP))
259: .getTimestampNoCopy();
260: }
261:
262: public byte[] getBytes() throws SQLException {
263: return ((ValueBytes) convertTo(Value.BYTES)).getBytes();
264: }
265:
266: public byte[] getBytesNoCopy() throws SQLException {
267: return ((ValueBytes) convertTo(Value.BYTES)).getBytesNoCopy();
268: }
269:
270: public byte getByte() throws SQLException {
271: return ((ValueByte) convertTo(Value.BYTE)).getByte();
272: }
273:
274: public short getShort() throws SQLException {
275: return ((ValueShort) convertTo(Value.SHORT)).getShort();
276: }
277:
278: public BigDecimal getBigDecimal() throws SQLException {
279: return ((ValueDecimal) convertTo(Value.DECIMAL))
280: .getBigDecimal();
281: }
282:
283: public double getDouble() throws SQLException {
284: return ((ValueDouble) convertTo(Value.DOUBLE)).getDouble();
285: }
286:
287: public float getFloat() throws SQLException {
288: return ((ValueFloat) convertTo(Value.FLOAT)).getFloat();
289: }
290:
291: public int getInt() throws SQLException {
292: return ((ValueInt) convertTo(Value.INT)).getInt();
293: }
294:
295: public long getLong() throws SQLException {
296: return ((ValueLong) convertTo(Value.LONG)).getLong();
297: }
298:
299: public InputStream getInputStream() throws SQLException {
300: return new ByteArrayInputStream(getBytesNoCopy());
301: }
302:
303: public Reader getReader() throws SQLException {
304: return IOUtils.getReader(getString());
305: }
306:
307: public Value add(Value v) throws SQLException {
308: throw Message.getUnsupportedException();
309: }
310:
311: public int getSignum() throws SQLException {
312: throw Message.getUnsupportedException();
313: }
314:
315: public Value negate() throws SQLException {
316: throw Message.getUnsupportedException();
317: }
318:
319: public Value subtract(Value v) throws SQLException {
320: throw Message.getUnsupportedException();
321: }
322:
323: public Value divide(Value v) throws SQLException {
324: throw Message.getUnsupportedException();
325: }
326:
327: public Value multiply(Value v) throws SQLException {
328: throw Message.getUnsupportedException();
329: }
330:
331: public Value convertTo(int type) throws SQLException {
332: // converting NULL done in ValueNull
333: // converting BLOB to CLOB and vice versa is done in ValueLob
334: if (getType() == type) {
335: return this ;
336: }
337: // decimal conversion
338: switch (type) {
339: case BOOLEAN: {
340: switch (getType()) {
341: case BYTE:
342: case SHORT:
343: case INT:
344: case LONG:
345: case DECIMAL:
346: case DOUBLE:
347: case FLOAT:
348: return ValueBoolean.get(getSignum() != 0);
349: case TIME:
350: case DATE:
351: case TIMESTAMP:
352: case BYTES:
353: case JAVA_OBJECT:
354: case UUID:
355: throw Message.getSQLException(
356: ErrorCode.DATA_CONVERSION_ERROR_1, getString());
357: }
358: break;
359: }
360: case BYTE: {
361: switch (getType()) {
362: case BOOLEAN:
363: return ValueByte
364: .get(getBoolean().booleanValue() ? (byte) 1
365: : (byte) 0);
366: case SHORT:
367: return ValueByte.get(convertToByte(getShort()));
368: case INT:
369: return ValueByte.get(convertToByte(getInt()));
370: case LONG:
371: return ValueByte.get(convertToByte(getLong()));
372: case DECIMAL:
373: return ValueByte
374: .get(convertToByte(convertToLong(getBigDecimal())));
375: case DOUBLE:
376: return ValueByte
377: .get(convertToByte(convertToLong(getDouble())));
378: case FLOAT:
379: return ValueByte
380: .get(convertToByte(convertToLong(getFloat())));
381: }
382: break;
383: }
384: case SHORT: {
385: switch (getType()) {
386: case BOOLEAN:
387: return ValueShort
388: .get(getBoolean().booleanValue() ? (short) 1
389: : (short) 0);
390: case BYTE:
391: return ValueShort.get(getByte());
392: case INT:
393: return ValueShort.get(convertToShort(getInt()));
394: case LONG:
395: return ValueShort.get(convertToShort(getLong()));
396: case DECIMAL:
397: return ValueShort
398: .get(convertToShort(convertToLong(getBigDecimal())));
399: case DOUBLE:
400: return ValueShort
401: .get(convertToShort(convertToLong(getDouble())));
402: case FLOAT:
403: return ValueShort
404: .get(convertToShort(convertToLong(getFloat())));
405: }
406: break;
407: }
408: case INT: {
409: switch (getType()) {
410: case BOOLEAN:
411: return ValueInt
412: .get(getBoolean().booleanValue() ? 1 : 0);
413: case BYTE:
414: return ValueInt.get(getByte());
415: case SHORT:
416: return ValueInt.get(getShort());
417: case LONG:
418: return ValueInt.get(convertToInt(getLong()));
419: case DECIMAL:
420: return ValueInt
421: .get(convertToInt(convertToLong(getBigDecimal())));
422: case DOUBLE:
423: return ValueInt
424: .get(convertToInt(convertToLong(getDouble())));
425: case FLOAT:
426: return ValueInt
427: .get(convertToInt(convertToLong(getFloat())));
428: }
429: break;
430: }
431: case LONG: {
432: switch (getType()) {
433: case BOOLEAN:
434: return ValueLong.get(getBoolean().booleanValue() ? 1
435: : 0);
436: case BYTE:
437: return ValueLong.get(getByte());
438: case SHORT:
439: return ValueLong.get(getShort());
440: case INT:
441: return ValueLong.get(getInt());
442: case DECIMAL:
443: return ValueLong.get(convertToLong(getBigDecimal()));
444: case DOUBLE:
445: return ValueLong.get(convertToLong(getDouble()));
446: case FLOAT:
447: return ValueLong.get(convertToLong(getFloat()));
448: }
449: break;
450: }
451: case DECIMAL: {
452: // convert to string is required for JDK 1.4
453: switch (getType()) {
454: case BOOLEAN:
455: return ValueDecimal.get(new BigDecimal(getBoolean()
456: .booleanValue() ? "1" : "0"));
457: case BYTE:
458: return ValueDecimal.get(new BigDecimal("" + getByte()));
459: case SHORT:
460: return ValueDecimal
461: .get(new BigDecimal("" + getShort()));
462: case INT:
463: return ValueDecimal.get(new BigDecimal("" + getInt()));
464: case LONG:
465: return ValueDecimal.get(new BigDecimal("" + getLong()));
466: case DOUBLE: {
467: double d = getDouble();
468: if (Double.isInfinite(d) || Double.isNaN(d)) {
469: throw Message.getSQLException(
470: ErrorCode.DATA_CONVERSION_ERROR_1, "" + d);
471: }
472: return ValueDecimal.get(new BigDecimal(d));
473: }
474: case FLOAT: {
475: float f = getFloat();
476: if (Float.isInfinite(f) || Float.isNaN(f)) {
477: throw Message.getSQLException(
478: ErrorCode.DATA_CONVERSION_ERROR_1, "" + f);
479: }
480: return ValueDecimal.get(new BigDecimal(f));
481: }
482: }
483: break;
484: }
485: case DOUBLE: {
486: switch (getType()) {
487: case BOOLEAN:
488: return ValueDouble.get(getBoolean().booleanValue() ? 1
489: : 0);
490: case BYTE:
491: return ValueDouble.get(getByte());
492: case SHORT:
493: return ValueDouble.get(getShort());
494: case INT:
495: return ValueDouble.get(getInt());
496: case LONG:
497: return ValueDouble.get(getLong());
498: case DECIMAL:
499: return ValueDouble.get(getBigDecimal().doubleValue());
500: case FLOAT:
501: return ValueDouble.get(getFloat());
502: }
503: break;
504: }
505: case FLOAT: {
506: switch (getType()) {
507: case BOOLEAN:
508: return ValueFloat.get(getBoolean().booleanValue() ? 1
509: : 0);
510: case BYTE:
511: return ValueFloat.get(getByte());
512: case SHORT:
513: return ValueFloat.get(getShort());
514: case INT:
515: return ValueFloat.get(getInt());
516: case LONG:
517: return ValueFloat.get(getLong());
518: case DECIMAL:
519: return ValueFloat.get(getBigDecimal().floatValue());
520: case DOUBLE:
521: return ValueFloat.get((float) getDouble());
522: }
523: break;
524: }
525: case DATE: {
526: switch (getType()) {
527: case TIME:
528: return ValueDate
529: .get(new Date(getTimeNoCopy().getTime()));
530: case TIMESTAMP:
531: return ValueDate.get(new Date(getTimestampNoCopy()
532: .getTime()));
533: }
534: break;
535: }
536: case TIME: {
537: switch (getType()) {
538: case DATE:
539: // need to normalize the year, month and day
540: return ValueTime
541: .get(new Time(getDateNoCopy().getTime()));
542: case TIMESTAMP:
543: // need to normalize the year, month and day
544: return ValueTime.get(new Time(getTimestampNoCopy()
545: .getTime()));
546: }
547: break;
548: }
549: case TIMESTAMP: {
550: switch (getType()) {
551: case TIME:
552: return ValueTimestamp.getNoCopy(new Timestamp(
553: getTimeNoCopy().getTime()));
554: case DATE:
555: return ValueTimestamp.getNoCopy(new Timestamp(
556: getDateNoCopy().getTime()));
557: }
558: break;
559: }
560: case BYTES: {
561: switch (getType()) {
562: case JAVA_OBJECT:
563: case BLOB:
564: case UUID:
565: return ValueBytes.getNoCopy(getBytesNoCopy());
566: }
567: break;
568: }
569: case JAVA_OBJECT: {
570: switch (getType()) {
571: case BYTES:
572: case BLOB:
573: return ValueBytes.getNoCopy(getBytesNoCopy());
574: }
575: break;
576: }
577: case BLOB: {
578: switch (getType()) {
579: case BYTES:
580: return ValueLob.createSmallLob(Value.BLOB,
581: getBytesNoCopy());
582: }
583: break;
584: }
585: case UUID: {
586: switch (getType()) {
587: case BYTES:
588: return ValueUuid.get(getBytesNoCopy());
589: }
590: }
591: }
592: // conversion by parsing the string value
593: String s = getString();
594: try {
595: switch (type) {
596: case NULL:
597: return ValueNull.INSTANCE;
598: case BOOLEAN: {
599: if (s.equalsIgnoreCase("true")
600: || s.equalsIgnoreCase("t")
601: || s.equalsIgnoreCase("yes")
602: || s.equalsIgnoreCase("y")) {
603: return ValueBoolean.get(true);
604: } else if (s.equalsIgnoreCase("false")
605: || s.equalsIgnoreCase("f")
606: || s.equalsIgnoreCase("no")
607: || s.equalsIgnoreCase("n")) {
608: return ValueBoolean.get(false);
609: } else {
610: // convert to a number, and if it is not 0 then it is true
611: return ValueBoolean
612: .get(new BigDecimal(s).signum() != 0);
613: }
614: }
615: case BYTE:
616: return ValueByte.get(MathUtils.decodeByte(s.trim()));
617: case SHORT:
618: return ValueShort.get(MathUtils.decodeShort(s.trim()));
619: case INT:
620: return ValueInt.get(MathUtils.decodeInt(s.trim()));
621: case LONG:
622: return ValueLong.get(MathUtils.decodeLong(s.trim()));
623: case DECIMAL:
624: return ValueDecimal.get(new BigDecimal(s.trim()));
625: case TIME:
626: return ValueTime.get(ValueTime.parseTime(s.trim()));
627: case DATE:
628: return ValueDate.get(ValueDate.parseDate(s.trim()));
629: case TIMESTAMP:
630: return ValueTimestamp.get(ValueTimestamp
631: .parseTimestamp(s.trim()));
632: case BYTES:
633: return ValueBytes.getNoCopy(ByteUtils
634: .convertStringToBytes(s.trim()));
635: case JAVA_OBJECT:
636: return ValueJavaObject.getNoCopy(ByteUtils
637: .convertStringToBytes(s.trim()));
638: case STRING:
639: return ValueString.get(s);
640: case STRING_IGNORECASE:
641: return ValueStringIgnoreCase.get(s);
642: case STRING_FIXED:
643: return ValueStringFixed.get(s);
644: case DOUBLE:
645: return ValueDouble.get(Double.parseDouble(s.trim()));
646: case FLOAT:
647: return ValueFloat.get(Float.parseFloat(s.trim()));
648: case CLOB:
649: return ValueLob.createSmallLob(CLOB, StringUtils
650: .utf8Encode(s));
651: case BLOB:
652: return ValueLob.createSmallLob(BLOB, ByteUtils
653: .convertStringToBytes(s.trim()));
654: case ARRAY:
655: return ValueArray
656: .get(new Value[] { ValueString.get(s) });
657: case RESULT_SET: {
658: SimpleResultSet rs = new SimpleResultSet();
659: rs.addColumn("X", Types.VARCHAR, s.length(), 0);
660: rs.addRow(new String[] { s });
661: return ValueResultSet.get(rs);
662: }
663: case UUID:
664: return ValueUuid.get(s);
665: default:
666: throw Message.getInternalError("type=" + type);
667: }
668: } catch (NumberFormatException e) {
669: throw Message.getSQLException(
670: ErrorCode.DATA_CONVERSION_ERROR_1,
671: new String[] { s }, e);
672: }
673: }
674:
675: public final int compareTypeSave(Value v, CompareMode mode)
676: throws SQLException {
677: if (this == ValueNull.INSTANCE) {
678: return v == ValueNull.INSTANCE ? 0 : -1;
679: } else if (v == ValueNull.INSTANCE) {
680: return 1;
681: }
682: return compareSecure(v, mode);
683: }
684:
685: public final boolean compareEqual(Value v) throws SQLException {
686: if (this == ValueNull.INSTANCE) {
687: return v == ValueNull.INSTANCE;
688: } else if (v == ValueNull.INSTANCE) {
689: return false;
690: }
691: if (getType() == v.getType()) {
692: return equals(v);
693: }
694: int t2 = Value.getHigherOrder(getType(), v.getType());
695: return convertTo(t2).equals(v.convertTo(t2));
696: }
697:
698: public final int compareTo(Value v, CompareMode mode)
699: throws SQLException {
700: if (this == ValueNull.INSTANCE) {
701: return v == ValueNull.INSTANCE ? 0 : -1;
702: } else if (v == ValueNull.INSTANCE) {
703: return 1;
704: }
705: if (getType() == v.getType()) {
706: return compareSecure(v, mode);
707: }
708: int t2 = Value.getHigherOrder(getType(), v.getType());
709: return convertTo(t2).compareSecure(v.convertTo(t2), mode);
710: }
711:
712: public int getScale() {
713: return 0;
714: }
715:
716: public Value convertScale(boolean onlyToSmallerScale,
717: int targetScale) throws SQLException {
718: return this ;
719: }
720:
721: public Value convertPrecision(long precision) throws SQLException {
722: return this ;
723: }
724:
725: private byte convertToByte(long x) throws SQLException {
726: if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) {
727: throw Message
728: .getSQLException(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
729: }
730: return (byte) x;
731: }
732:
733: private short convertToShort(long x) throws SQLException {
734: if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) {
735: throw Message
736: .getSQLException(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
737: }
738: return (short) x;
739: }
740:
741: private int convertToInt(long x) throws SQLException {
742: if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) {
743: throw Message
744: .getSQLException(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
745: }
746: return (int) x;
747: }
748:
749: private long convertToLong(double x) throws SQLException {
750: if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) {
751: // TODO document that +Infinity, -Infinity throw an exception and NaN returns 0
752: throw Message
753: .getSQLException(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
754: }
755: if (Constants.CONVERT_TO_LONG_ROUND) {
756: return Math.round(x);
757: } else {
758: return (long) x;
759: }
760: }
761:
762: private long convertToLong(BigDecimal x) throws SQLException {
763: if (x.compareTo(MAX_LONG_DECIMAL) > 0
764: || x.compareTo(Value.MIN_LONG_DECIMAL) < 0) {
765: throw Message
766: .getSQLException(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
767: }
768: if (Constants.CONVERT_TO_LONG_ROUND) {
769: return x.setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
770: } else {
771: return x.longValue();
772: }
773: }
774:
775: public Value link(DataHandler handler, int tableId)
776: throws SQLException {
777: return this ;
778: }
779:
780: public boolean isLinked() {
781: return false;
782: }
783:
784: public void unlink() throws SQLException {
785: }
786:
787: public boolean isFileBased() {
788: return false;
789: }
790:
791: public void close() throws SQLException {
792: }
793:
794: public boolean checkPrecision(long precision) {
795: return getPrecision() <= precision;
796: }
797:
798: public String toString() {
799: return getSQL();
800: }
801:
802: }
|