001: /*
002:
003: Derby - Class org.apache.derby.iapi.types.SQLReal
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.types;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.io.ArrayInputStream;
027:
028: import org.apache.derby.iapi.types.BooleanDataValue;
029: import org.apache.derby.iapi.types.DataValueDescriptor;
030: import org.apache.derby.iapi.types.NumberDataValue;
031: import org.apache.derby.iapi.types.StringDataValue;
032: import org.apache.derby.iapi.types.TypeId;
033:
034: import org.apache.derby.iapi.services.io.Storable;
035: import org.apache.derby.iapi.services.io.StoredFormatIds;
036:
037: import org.apache.derby.iapi.error.StandardException;
038:
039: import org.apache.derby.iapi.services.sanity.SanityManager;
040: import org.apache.derby.iapi.types.NumberDataType;
041: import org.apache.derby.iapi.types.SQLBoolean;
042:
043: import org.apache.derby.iapi.services.cache.ClassSize;
044:
045: import java.io.ObjectOutput;
046: import java.io.ObjectInput;
047: import java.io.IOException;
048:
049: import java.sql.ResultSet;
050: import java.sql.PreparedStatement;
051: import java.sql.SQLException;
052:
053: /**
054: * SQLReal satisfies the DataValueDescriptor
055: * interfaces (i.e., OrderableDataType). It implements a real column,
056: * e.g. for storing a column value; it can be specified
057: * when constructed to not allow nulls. Nullability cannot be changed
058: * after construction, as it affects the storage size and mechanism.
059: * <p>
060: * Because OrderableDataType is a subtype of ValueColumn,
061: * SQLReal can play a role in either a ValueColumn/Row
062: * or a OrderableDataType/Row, interchangeably.
063: * <p>
064: * We assume the store has a flag for nullness of the value,
065: * and simply return a 0-length array for the stored form
066: * when the value is null.
067: * <p>
068: * PERFORMANCE: There are likely alot of performance improvements
069: * possible for this implementation -- it new's Float
070: * more than it probably wants to.
071: * <p>
072: * This is called SQLReal even though it maps to the Java float type,
073: * to avoid confusion with whether it maps to the SQL float type or not.
074: * It doesn't, it maps to the SQL real type.
075: * <p>
076: * This is modeled after SQLSmallint.
077: * @see SQLSmallint
078: *
079: * @author ames
080: */
081: public final class SQLReal extends NumberDataType {
082:
083: /*
084: * DataValueDescriptor interface
085: * (mostly implemented in DataType)
086: */
087:
088: // JDBC is lax in what it permits and what it
089: // returns, so we are similarly lax
090: /**
091: * @see DataValueDescriptor#getInt
092: * @exception StandardException thrown on failure to convert
093: */
094: public int getInt() throws StandardException {
095: if ((value > (((double) Integer.MAX_VALUE + 1.0d)))
096: || (value < (((double) Integer.MIN_VALUE) - 1.0d)))
097: throw StandardException
098: .newException(
099: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
100: "INTEGER");
101: return (int) value;
102: }
103:
104: /**
105: * @see DataValueDescriptor#getByte
106: * @exception StandardException thrown on failure to convert
107: */
108: public byte getByte() throws StandardException {
109: if ((value > (((double) Byte.MAX_VALUE + 1.0d)))
110: || (value < (((double) Byte.MIN_VALUE) - 1.0d)))
111: throw StandardException
112: .newException(
113: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
114: "TINYINT");
115: return (byte) value;
116: }
117:
118: /**
119: * @exception StandardException thrown on failure to convert
120: * @see DataValueDescriptor#getShort
121: */
122: public short getShort() throws StandardException {
123: if ((value > (((double) Short.MAX_VALUE + 1.0d)))
124: || (value < (((double) Short.MIN_VALUE) - 1.0d)))
125: throw StandardException.newException(
126: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
127: "SMALLINT");
128: return (short) value;
129: }
130:
131: /**
132: * @see DataValueDescriptor#getLong
133: * @exception StandardException thrown on failure to convert
134: */
135: public long getLong() throws StandardException {
136: if ((value > (((double) Long.MAX_VALUE + 1.0d)))
137: || (value < (((double) Long.MIN_VALUE) - 1.0d)))
138: throw StandardException.newException(
139: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "BIGINT");
140: return (long) value;
141: }
142:
143: /**
144: * @see DataValueDescriptor#getFloat
145: */
146: public float getFloat() {
147: return value;
148: }
149:
150: /**
151: * @see DataValueDescriptor#getDouble
152: */
153: public double getDouble() {
154: return (double) value;
155: }
156:
157: /**
158: * DOUBLE implementation. Convert to a BigDecimal using getString.
159: */
160: public int typeToBigDecimal() {
161: return java.sql.Types.CHAR;
162: }
163:
164: // for lack of a specification: 0 or null is false,
165: // all else is true
166: /**
167: * @see DataValueDescriptor#getBoolean
168: */
169: public boolean getBoolean() {
170: return (value != 0);
171: }
172:
173: /**
174: * @see DataValueDescriptor#getString
175: */
176: public String getString() {
177: if (isNull())
178: return null;
179: else
180: return Float.toString(value);
181: }
182:
183: /**
184: * @see DataValueDescriptor#getLength
185: */
186: public int getLength() {
187: return REAL_LENGTH;
188: }
189:
190: /**
191: * @see DataValueDescriptor#getObject
192: */
193: public Object getObject() {
194: if (isNull())
195: return null;
196: else
197: return new Float(value);
198: }
199:
200: // this is for DataType's error generator
201: public String getTypeName() {
202: return TypeId.REAL_NAME;
203: }
204:
205: /*
206: * Storable interface, implies Externalizable, TypedFormat
207: */
208:
209: /**
210: Return my format identifier.
211:
212: @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
213: */
214: public int getTypeFormatId() {
215: return StoredFormatIds.SQL_REAL_ID;
216: }
217:
218: /*
219: * see if the real value is null.
220: */
221: /** @see Storable#isNull */
222: public boolean isNull() {
223: return isnull;
224: }
225:
226: public void writeExternal(ObjectOutput out) throws IOException {
227:
228: // never called when value is null
229: if (SanityManager.DEBUG)
230: SanityManager.ASSERT(!isNull());
231:
232: out.writeFloat(value);
233: }
234:
235: /** @see java.io.Externalizable#readExternal */
236: public void readExternal(ObjectInput in) throws IOException {
237: // setValue(in.readFloat()); // can throw StandardException which we can't pass on
238: // assume we wrote the value, so we can read it without problem, for now.
239: value = in.readFloat();
240: isnull = false;
241: }
242:
243: public void readExternalFromArray(ArrayInputStream in)
244: throws IOException {
245: // setValue(in.readFloat()); // can throw StandardException which we can't pass on
246: // assume we wrote the value, so we can read it without problem, for now.
247: value = in.readFloat();
248: isnull = false;
249: }
250:
251: /**
252: * @see Storable#restoreToNull
253: *
254: */
255: public void restoreToNull() {
256: value = 0;
257: isnull = true;
258: }
259:
260: /** @exception StandardException Thrown on error */
261: protected int typeCompare(DataValueDescriptor arg)
262: throws StandardException {
263: /* neither are null, get the value */
264:
265: // jsk: should use double? depends on DB2
266: float this Value = this .getFloat();
267: float otherValue = NumberDataType.normalizeREAL(arg.getFloat()); // could gotten from "any type", may not be a float
268:
269: if (this Value == otherValue)
270: return 0;
271: else if (this Value > otherValue)
272: return 1;
273: else
274: return -1;
275: }
276:
277: /*
278: * DataValueDescriptor interface
279: */
280:
281: /** @see DataValueDescriptor#getClone */
282: public DataValueDescriptor getClone() {
283: SQLReal ret = new SQLReal();
284: ret.value = this .value;
285: ret.isnull = this .isnull;
286: return ret;
287: }
288:
289: /**
290: * @see DataValueDescriptor#getNewNull
291: */
292: public DataValueDescriptor getNewNull() {
293: return new SQLReal();
294: }
295:
296: /**
297: * @see DataValueDescriptor#setValueFromResultSet
298: *
299: * @exception StandardException Thrown on error
300: * @exception SQLException Thrown on error
301: */
302: public void setValueFromResultSet(ResultSet resultSet,
303: int colNumber, boolean isNullable)
304: throws StandardException, SQLException {
305: float fv = resultSet.getFloat(colNumber);
306: if (isNullable && resultSet.wasNull())
307: restoreToNull();
308: else
309: setValue(fv);
310: }
311:
312: /**
313: Set the value into a PreparedStatement.
314:
315: @exception SQLException Error setting value in PreparedStatement
316: */
317: public final void setInto(PreparedStatement ps, int position)
318: throws SQLException {
319:
320: if (isNull()) {
321: ps.setNull(position, java.sql.Types.REAL);
322: return;
323: }
324:
325: ps.setFloat(position, value);
326: }
327:
328: /**
329: Set this value into a ResultSet for a subsequent ResultSet.insertRow
330: or ResultSet.updateRow. This method will only be called for non-null values.
331:
332: @exception SQLException thrown by the ResultSet object
333: @exception StandardException thrown by me accessing my value.
334: */
335: public final void setInto(ResultSet rs, int position)
336: throws SQLException, StandardException {
337: rs.updateFloat(position, value);
338: }
339:
340: /*
341: * class interface
342: */
343:
344: /*
345: * constructors
346: */
347:
348: /** no-arg constructor, required by Formattable. */
349: // This constructor also gets used when we are
350: // allocating space for a float.
351: public SQLReal() {
352: isnull = true;
353: }
354:
355: public SQLReal(float val) throws StandardException {
356: value = NumberDataType.normalizeREAL(val);
357: }
358:
359: public SQLReal(Float obj) throws StandardException {
360: if (isnull = (obj == null))
361: ;
362: else {
363: value = NumberDataType.normalizeREAL(obj.floatValue());
364: }
365: }
366:
367: /**
368: @exception StandardException thrown if string not accepted
369: */
370: public void setValue(String theValue) throws StandardException {
371: if (theValue == null) {
372: value = 0;
373: isnull = true;
374: } else {
375: // what if String is rouned to zero?
376: //System.out.println("SQLReal.setValue(String) - rounding issue?"+theValue);
377: try {
378: setValue(Double.valueOf(theValue.trim()).doubleValue());
379: } catch (NumberFormatException nfe) {
380: throw invalidFormat();
381: }
382: }
383: }
384:
385: public void setValue(Number theValue) throws StandardException {
386: if (objectNull(theValue))
387: return;
388:
389: if (SanityManager.ASSERT) {
390: if (!(theValue instanceof java.lang.Float))
391: SanityManager
392: .THROWASSERT("SQLReal.setValue(Number) passed a "
393: + theValue.getClass());
394: }
395:
396: setValue(theValue.floatValue());
397: }
398:
399: /**
400: Called for an application setting this value using a BigDecimal
401: */
402: public void setBigDecimal(Number bigDecimal)
403: throws StandardException {
404: if (objectNull(bigDecimal))
405: return;
406:
407: // Note BigDecimal.floatValue() handles the case where
408: // its value is outside the range of a float. It returns
409: // infinity values which should throw an exception in setValue(double).
410: setValue(bigDecimal.floatValue());
411:
412: }
413:
414: public void setValue(float theValue) throws StandardException {
415: value = NumberDataType.normalizeREAL(theValue);
416: isnull = false;
417: }
418:
419: public void setValue(int theValue) {
420: value = theValue;
421: isnull = false;
422:
423: }
424:
425: public void setValue(long theValue) {
426: value = theValue;
427: isnull = false;
428:
429: }
430:
431: /**
432: @exception StandardException if outsideRangeForReal
433: */
434: public void setValue(double theValue) throws StandardException {
435: // jsk: where does this theValue come from? if some caller is rounding parsing from string
436: // we might have rounding error (different than DB2 behaviour)
437: float fv = (float) theValue;
438: // detect rounding taking place at cast time
439: if (fv == 0.0f && theValue != 0.0d) {
440: throw StandardException.newException(
441: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
442: TypeId.REAL_NAME);
443: }
444: setValue(fv);
445: }
446:
447: /**
448: * @see NumberDataValue#setValue
449: *
450: */
451: public void setValue(boolean theValue) {
452: value = theValue ? 1 : 0;
453: isnull = false;
454: }
455:
456: /**
457: * Set the value from a correctly typed Float object.
458: * @throws StandardException
459: */
460: void setObject(Object theValue) throws StandardException {
461: setValue(((Float) theValue).floatValue());
462: }
463:
464: protected void setFrom(DataValueDescriptor theValue)
465: throws StandardException {
466:
467: // rounding issue to solve!!!/jsk
468: //DOUBLE.getFloat() would make rounding problem if it got called here!!!
469: // need to check where it is called from!
470: if (theValue instanceof StringDataValue) {
471: //System.out.println("\tcalling setValue(string)");
472: setValue(theValue.getString());
473: } else if (theValue instanceof SQLDouble) {
474: //System.out.println("\tcalling setValue(double)");
475: setValue(theValue.getDouble());
476: } else {
477: setValue(theValue.getFloat());
478: }
479: }
480:
481: /*
482: * DataValueDescriptor interface
483: */
484:
485: /** @see DataValueDescriptor#typePrecedence */
486: public int typePrecedence() {
487: return TypeId.REAL_PRECEDENCE;
488: }
489:
490: /*
491: ** SQL Operators
492: */
493:
494: /**
495: * The = operator as called from the language module, as opposed to
496: * the storage module.
497: *
498: * @param left The value on the left side of the =
499: * @param right The value on the right side of the =
500: *
501: * @return A SQL boolean value telling whether the two parameters are equal
502: *
503: * @exception StandardException Thrown on error
504: */
505:
506: public BooleanDataValue equals(DataValueDescriptor left,
507: DataValueDescriptor right) throws StandardException {
508: return SQLBoolean.truthValue(left, right,
509: left.getFloat() == right.getFloat());
510: }
511:
512: /**
513: * The <> operator as called from the language module, as opposed to
514: * the storage module.
515: *
516: * @param left The value on the left side of the <>
517: * @param right The value on the right side of the <>
518: *
519: * @return A SQL boolean value telling whether the two parameters
520: * are not equal
521: *
522: * @exception StandardException Thrown on error
523: */
524:
525: public BooleanDataValue notEquals(DataValueDescriptor left,
526: DataValueDescriptor right) throws StandardException {
527: return SQLBoolean.truthValue(left, right,
528: left.getFloat() != right.getFloat());
529: }
530:
531: /**
532: * The < operator as called from the language module, as opposed to
533: * the storage module.
534: *
535: * @param left The value on the left side of the <
536: * @param right The value on the right side of the <
537: *
538: * @return A SQL boolean value telling whether the first operand is less
539: * than the second operand
540: *
541: * @exception StandardException Thrown on error
542: */
543:
544: public BooleanDataValue lessThan(DataValueDescriptor left,
545: DataValueDescriptor right) throws StandardException {
546: return SQLBoolean.truthValue(left, right,
547: left.getFloat() < right.getFloat());
548: }
549:
550: /**
551: * The > operator as called from the language module, as opposed to
552: * the storage module.
553: *
554: * @param left The value on the left side of the >
555: * @param right The value on the right side of the >
556: *
557: * @return A SQL boolean value telling whether the first operand is greater
558: * than the second operand
559: *
560: * @exception StandardException Thrown on error
561: */
562:
563: public BooleanDataValue greaterThan(DataValueDescriptor left,
564: DataValueDescriptor right) throws StandardException {
565: return SQLBoolean.truthValue(left, right,
566: left.getFloat() > right.getFloat());
567: }
568:
569: /**
570: * The <= operator as called from the language module, as opposed to
571: * the storage module.
572: *
573: * @param left The value on the left side of the <=
574: * @param right The value on the right side of the <=
575: *
576: * @return A SQL boolean value telling whether the first operand is less
577: * than or equal to the second operand
578: *
579: * @exception StandardException Thrown on error
580: */
581:
582: public BooleanDataValue lessOrEquals(DataValueDescriptor left,
583: DataValueDescriptor right) throws StandardException {
584: return SQLBoolean.truthValue(left, right,
585: left.getFloat() <= right.getFloat());
586: }
587:
588: /**
589: * The >= operator as called from the language module, as opposed to
590: * the storage module.
591: *
592: * @param left The value on the left side of the >=
593: * @param right The value on the right side of the >=
594: *
595: * @return A SQL boolean value telling whether the first operand is greater
596: * than or equal to the second operand
597: *
598: * @exception StandardException Thrown on error
599: */
600:
601: public BooleanDataValue greaterOrEquals(DataValueDescriptor left,
602: DataValueDescriptor right) throws StandardException {
603: return SQLBoolean.truthValue(left, right,
604: left.getFloat() >= right.getFloat());
605: }
606:
607: /**
608: * This method implements the + operator for "real + real".
609: * The operator uses DOUBLE aritmetic as DB2 does.
610: *
611: * @param addend1 One of the addends
612: * @param addend2 The other addend
613: * @param result The result of a previous call to this method, null
614: * if not called yet
615: *
616: * @return A SQLReal containing the result of the addition
617: *
618: * @exception StandardException Thrown on error
619: */
620:
621: public NumberDataValue plus(NumberDataValue addend1,
622: NumberDataValue addend2, NumberDataValue result)
623: throws StandardException {
624: if (result == null) {
625: result = new SQLReal();
626: }
627:
628: if (addend1.isNull() || addend2.isNull()) {
629: result.setToNull();
630: return result;
631: }
632:
633: double dsum = addend1.getDouble() + addend2.getDouble();
634: // No need to check underflow (result rounded to 0.0),
635: // since the difference between two non-equal valid DB2 DOUBLE values is always non-zero in java.lang.Double precision.
636: result.setValue(dsum);
637:
638: return result;
639: }
640:
641: /**
642: * This method implements the - operator for "real - real".
643: * The operator uses DOUBLE aritmetic as DB2 does.
644: *
645: * @param left The value to be subtracted from
646: * @param right The value to be subtracted
647: * @param result The result of a previous call to this method, null
648: * if not called yet
649: *
650: * @return A SQLReal containing the result of the subtraction
651: *
652: * @exception StandardException Thrown on error
653: */
654:
655: public NumberDataValue minus(NumberDataValue left,
656: NumberDataValue right, NumberDataValue result)
657: throws StandardException {
658: if (result == null) {
659: result = new SQLReal();
660: }
661:
662: if (left.isNull() || right.isNull()) {
663: result.setToNull();
664: return result;
665: }
666:
667: double ddifference = left.getDouble() - right.getDouble();
668: // No need to check underflow (result rounded to 0.0),
669: // since no difference between two valid DB2 DOUBLE values can be rounded off to 0.0 in java.lang.Double
670: result.setValue(ddifference);
671: return result;
672: }
673:
674: /**
675: * This method implements the * operator for "real * real".
676: * The operator uses DOUBLE aritmetic as DB2 does.
677: *
678: * @param left The first value to be multiplied
679: * @param right The second value to be multiplied
680: * @param result The result of a previous call to this method, null
681: * if not called yet
682: *
683: * @return A SQLReal containing the result of the multiplication
684: *
685: * @exception StandardException Thrown on error
686: */
687:
688: public NumberDataValue times(NumberDataValue left,
689: NumberDataValue right, NumberDataValue result)
690: throws StandardException {
691: if (result == null) {
692: result = new SQLReal();
693: }
694:
695: if (left.isNull() || right.isNull()) {
696: result.setToNull();
697: return result;
698: }
699:
700: double leftValue = left.getDouble();
701: double rightValue = right.getDouble();
702: double tempResult = leftValue * rightValue;
703: // check underflow (result rounded to 0.0)
704: if ((tempResult == 0.0)
705: && ((leftValue != 0.0) && (rightValue != 0.0))) {
706: throw StandardException.newException(
707: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
708: TypeId.REAL_NAME);
709: }
710:
711: result.setValue(tempResult);
712: return result;
713: }
714:
715: /**
716: * This method implements the / operator for "real / real".
717: * The operator uses DOUBLE aritmetic as DB2 does.
718: *
719: * @param dividend The numerator
720: * @param divisor The denominator
721: * @param result The result of a previous call to this method, null
722: * if not called yet
723: *
724: * @return A SQLReal containing the result of the division
725: *
726: * @exception StandardException Thrown on error
727: */
728:
729: public NumberDataValue divide(NumberDataValue dividend,
730: NumberDataValue divisor, NumberDataValue result)
731: throws StandardException {
732: if (result == null) {
733: result = new SQLReal();
734: }
735:
736: if (dividend.isNull() || divisor.isNull()) {
737: result.setToNull();
738: return result;
739: }
740:
741: double divisorValue = divisor.getDouble();
742: if (divisorValue == 0.0e0f) {
743: throw StandardException
744: .newException(SQLState.LANG_DIVIDE_BY_ZERO);
745: }
746:
747: double dividendValue = dividend.getDouble();
748: double resultValue = dividendValue / divisorValue;
749: if (Double.isNaN(resultValue)) {
750: throw StandardException
751: .newException(SQLState.LANG_DIVIDE_BY_ZERO);
752: }
753:
754: // check underflow (result rounded to 0.0)
755: if ((resultValue == 0.0e0d) && (dividendValue != 0.0e0d)) {
756: throw StandardException.newException(
757: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
758: TypeId.REAL_NAME);
759: }
760:
761: result.setValue(resultValue);
762: return result;
763: }
764:
765: /**
766: * This method implements the unary minus operator for real.
767: *
768: * @param result The result of a previous call to this method, null
769: * if not called yet
770: *
771: * @return A SQLSmalllint containing the result of the division
772: *
773: * @exception StandardException Thrown on error
774: */
775:
776: public NumberDataValue minus(NumberDataValue result)
777: throws StandardException {
778: float minusResult;
779:
780: if (result == null) {
781: result = new SQLReal();
782: }
783:
784: if (this .isNull()) {
785: result.setToNull();
786: return result;
787: }
788:
789: minusResult = -(this .getFloat());
790: result.setValue(minusResult);
791: return result;
792: }
793:
794: /**
795: * This method implements the isNegative method.
796: * Note: This method will return true for -0.0f.
797: *
798: * @return A boolean. If this.value is negative, return true.
799: * For positive values or null, return false.
800: */
801:
802: protected boolean isNegative() {
803: return !isNull() && (value < 0.0f);
804: }
805:
806: /*
807: * String display of value
808: */
809:
810: public String toString() {
811: if (isNull())
812: return "NULL";
813: else
814: return Float.toString(value);
815: }
816:
817: /*
818: * Hash code
819: */
820: public int hashCode() {
821: long longVal = (long) value;
822:
823: if (longVal != value) {
824: longVal = Double.doubleToLongBits(value);
825: }
826:
827: return (int) (longVal ^ (longVal >> 32));
828: }
829:
830: static final int REAL_LENGTH = 16;
831:
832: private static final int BASE_MEMORY_USAGE = ClassSize
833: .estimateBaseFromCatalog(SQLReal.class);
834:
835: public int estimateMemoryUsage() {
836: return BASE_MEMORY_USAGE;
837: }
838:
839: /*
840: * object state
841: */
842: private float value;
843: private boolean isnull;
844:
845: }
|