001: /*
002:
003: Derby - Class org.apache.derby.iapi.types.BinaryDecimal
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 java.io.IOException;
025: import java.io.ObjectInput;
026: import java.io.ObjectOutput;
027: import java.sql.ResultSet;
028: import java.sql.SQLException;
029: import java.sql.Types;
030:
031: import org.apache.derby.iapi.error.StandardException;
032: import org.apache.derby.iapi.reference.SQLState;
033: import org.apache.derby.iapi.services.io.ArrayInputStream;
034: import org.apache.derby.iapi.services.io.StoredFormatIds;
035: import org.apache.derby.iapi.services.sanity.SanityManager;
036:
037: /**
038: * SQL DECIMAL using raw data. Provides the basis for the
039: * CDCDecimal implementation.
040: * <P>
041: * The on-disk format must match the SQLDecimal format so that
042: * databases are portable across J2ME and J2SE environments.
043: * <P>
044: * The format of the byte array is defined by the return of the
045: * java.math.BigInteger.toByteArray:, extracted here.
046: *
047: * Returns a byte array containing the two's-complement representation of this BigInteger.
048: * The byte array will be in big-endian byte-order: the most significant byte is in the zeroth element.
049: *
050: * This is the format for DECIMAL even if BigINteger is not available, e.g. OSGi ee.minimum.
051: */
052:
053: abstract class BinaryDecimal extends NumberDataType implements
054: VariableSizeDataValue {
055: /**
056: * An unscaled value of 1 in two's complement
057: */
058: private static final byte[] ONE_2C = { (byte) 0x01 };
059:
060: /**
061: * The unscaled value as a binary two's complement array.
062: */
063: protected byte[] data2c;
064:
065: /**
066: * The SQL scale, zero or positive, of the value
067: */
068: protected int sqlScale;
069:
070: BinaryDecimal() {
071: }
072:
073: /*
074: ** Methods about the DECIMAL type itself.
075: */
076:
077: /**
078: * DECIMAL implementation.
079: * Use DECIMAL to indicate to self that another
080: * passed in value is an instance of this type.
081: */
082: public final int typeToBigDecimal() {
083: return java.sql.Types.DECIMAL;
084: }
085:
086: /** @see DataValueDescriptor#typePrecedence */
087: public final int typePrecedence() {
088: return TypeId.DECIMAL_PRECEDENCE;
089: }
090:
091: /* Return DECIMAL as the type name.
092: * @see org.apache.derby.iapi.types.DataValueDescriptor#getTypeName()
093: */
094: public final String getTypeName() {
095: return TypeId.DECIMAL_NAME;
096: }
097:
098: /**
099: * Return my format identifier.
100: *
101: * @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
102: */
103: public final int getTypeFormatId() {
104: return StoredFormatIds.SQL_DECIMAL_ID;
105: }
106:
107: /*
108: ** NULL handling.
109: */
110:
111: /**
112: * see if the decimal value is null.
113: */
114: public boolean isNull() {
115: return data2c == null;
116: }
117:
118: public void restoreToNull() {
119: data2c = null;
120: }
121:
122: /* Check the leftmost bit, if set the value is negative.
123: * NULL values return false.
124: * @see org.apache.derby.iapi.types.NumberDataType#isNegative()
125: */
126: protected boolean isNegative() {
127: return !isNull() && ((data2c[0] & 0x80) != 0);
128: }
129:
130: /*
131: ** Methods to convert values into this DECIMAL
132: */
133:
134: /**
135: * Set the value from a long.
136: */
137: public void setValue(long theValue) {
138: byte[] rd = data2c;
139: if (rd == null || rd.length < 8)
140: rd = new byte[8];
141:
142: rd[0] = (byte) (theValue >>> 56);
143: rd[1] = (byte) (theValue >>> 48);
144: rd[2] = (byte) (theValue >>> 40);
145: rd[3] = (byte) (theValue >>> 32);
146: rd[4] = (byte) (theValue >>> 24);
147: rd[5] = (byte) (theValue >>> 16);
148: rd[6] = (byte) (theValue >>> 8);
149: rd[7] = (byte) theValue;
150:
151: if (SanityManager.DEBUG) {
152: data2c = rd;
153: sqlScale = 0;
154: try {
155: if (theValue != getLong())
156: SanityManager
157: .THROWASSERT("BinaryDecimal invalid long conversion before reduce in "
158: + theValue + " out " + getLong());
159: } catch (StandardException se) {
160: SanityManager.THROWASSERT(se.toString());
161: }
162: }
163:
164: data2c = BinaryDecimal.reduceBytes2c(rd, 0, 8);
165: sqlScale = 0;
166:
167: if (SanityManager.DEBUG) {
168: try {
169: if (theValue != getLong())
170: SanityManager
171: .THROWASSERT("BinaryDecimal invalid long conversion after reduce in "
172: + theValue + " out " + getLong());
173: } catch (StandardException se) {
174: SanityManager.THROWASSERT(se.toString());
175: }
176: }
177: }
178:
179: /**
180: * Set the value from an int, just copy 'byte-by-byte'
181: * from the int to a four byte array. Then reduce.
182: * @see NumberDataValue#setValue
183: */
184: public final void setValue(int theValue) {
185: byte[] rd = data2c;
186: if (rd == null || rd.length < 4)
187: rd = new byte[4];
188:
189: rd[0] = (byte) (theValue >>> 24);
190: rd[1] = (byte) (theValue >>> 16);
191: rd[2] = (byte) (theValue >>> 8);
192: rd[3] = (byte) theValue;
193:
194: data2c = BinaryDecimal.reduceBytes2c(rd, 0, 4);
195: sqlScale = 0;
196: }
197:
198: /**
199: * Set the value from a boolean
200: */
201: public void setValue(boolean theValue) {
202: int intValue = theValue ? 1 : 0;
203: setValue(intValue);
204: }
205:
206: /**
207: * Convert from a double, normalize and then convert as a String.
208: *
209: * @exception StandardException Thrown on error
210: */
211: public final void setValue(double theValue)
212: throws StandardException {
213: setCoreValue(NumberDataType.normalizeDOUBLE(theValue));
214: }
215:
216: /**
217: * Convert from a float, normalize and then convert as a String.
218: *
219: */
220: public final void setValue(float theValue) throws StandardException {
221: setCoreValue((double) NumberDataType.normalizeREAL(theValue));
222: }
223:
224: private void setCoreValue(double theValue) throws StandardException {
225: setValue(Double.toString(theValue));
226: }
227:
228: /**
229: Called when setting a DECIMAL value internally or from
230: through a procedure or function.
231: Handles long in addition to BigDecimal to handle
232: identity being stored as a long but returned as a DECIMAL.
233: */
234: public void setValue(Number theValue) throws StandardException {
235: if (SanityManager.ASSERT) {
236: if (theValue != null
237: && !(theValue instanceof java.lang.Long))
238: SanityManager
239: .THROWASSERT("BinaryDecimal.setValue(Number) passed a "
240: + theValue.getClass());
241: }
242:
243: if (theValue == null)
244: setToNull();
245: else
246: setValue(theValue.longValue());
247: }
248:
249: /**
250: * Set this DECIMAL value from another DataValueDescriptor
251: */
252: protected void setFrom(DataValueDescriptor dvd)
253: throws StandardException {
254:
255: switch (dvd.typeToBigDecimal()) {
256: case Types.CHAR:
257: case Types.DECIMAL: // TODO : direct copy
258:
259: setValue(dvd.getString());
260: break;
261: case Types.BIGINT:
262: setValue(dvd.getLong());
263: break;
264: default:
265: super .setFrom(dvd);
266: }
267: }
268:
269: /*
270: ** Methods to get a value from this DECIMAL
271: */
272:
273: /**
274: * Return a int from this value.
275: *
276: * @exception StandardException
277: * this value is out of range for an int
278: */
279: public final int getInt() throws StandardException {
280: if (isNull())
281: return 0;
282:
283: try {
284: long lv = getLong();
285:
286: if ((lv >= Integer.MIN_VALUE) && (lv <= Integer.MAX_VALUE))
287: return (int) lv;
288:
289: } catch (StandardException se) {
290: // out of range error but with incorrect messgae (BIGINT)
291: // fall through to correct message
292: }
293:
294: throw StandardException.newException(
295: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
296: }
297:
298: /**
299: * Return a byte from this value.
300: *
301: * @exception StandardException
302: * this value is out of range for a short
303: */
304: public final byte getByte() throws StandardException {
305: if (isNull())
306: return (byte) 0;
307:
308: try {
309: long lv = getLong();
310:
311: if ((lv >= Byte.MIN_VALUE) && (lv <= Byte.MAX_VALUE))
312: return (byte) lv;
313:
314: } catch (StandardException se) {
315: // out of range error but with incorrect messgae (BIGINT)
316: // fall through to correct message
317: }
318:
319: throw StandardException.newException(
320: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "TINYINT");
321: }
322:
323: /**
324: * Return a short from this value.
325: * @exception StandardException this value is out of range for a short
326: */
327: public final short getShort() throws StandardException {
328: if (isNull())
329: return (short) 0;
330:
331: try {
332: long lv = getLong();
333:
334: if ((lv >= Short.MIN_VALUE) && (lv <= Short.MAX_VALUE))
335: return (short) lv;
336:
337: } catch (StandardException se) {
338: // out of range error but with incorrect messgae (BIGINT)
339: // fall through to correct message
340: }
341:
342: throw StandardException.newException(
343: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "SMALLINT");
344: }
345:
346: /*
347: ** DECIMAL arithmetic methods.
348: */
349:
350: /**
351: * This method implements the + operator for DECIMAL.
352: *
353: * @param addend1 One of the addends
354: * @param addend2 The other addend
355: * @param result The result of a previous call to this method, null
356: * if not called yet
357: *
358: * @return A SQLDecimal containing the result of the addition
359: *
360: * @exception StandardException Thrown on error
361: */
362:
363: public final NumberDataValue plus(NumberDataValue addend1,
364: NumberDataValue addend2, NumberDataValue result)
365: throws StandardException {
366: if (result == null) {
367: result = (NumberDataValue) getNewNull();
368: }
369:
370: if (addend1.isNull() || addend2.isNull()) {
371: result.setToNull();
372: return result;
373: }
374:
375: return plusNN(addend1, addend2, result);
376: }
377:
378: /* (non-Javadoc)
379: * @see org.apache.derby.iapi.types.NumberDataValue#times(org.apache.derby.iapi.types.NumberDataValue, org.apache.derby.iapi.types.NumberDataValue, org.apache.derby.iapi.types.NumberDataValue)
380: */
381: public final NumberDataValue times(NumberDataValue left,
382: NumberDataValue right, NumberDataValue result)
383: throws StandardException {
384: if (result == null) {
385: result = (NumberDataValue) getNewNull();
386: }
387:
388: if (left.isNull() || right.isNull()) {
389: result.setToNull();
390: return result;
391: }
392: return timesNN(left, right, result);
393: }
394:
395: public NumberDataValue divide(NumberDataValue dividend,
396: NumberDataValue divisor, NumberDataValue result)
397: throws StandardException {
398: return divide(dividend, divisor, result, -1);
399: }
400:
401: /**
402: * This method implements the / operator for BigDecimal/BigDecimal
403: *
404: * @param dividend The numerator
405: * @param divisor The denominator
406: * @param result The result of a previous call to this method, null
407: * if not called yet
408: * @param scale The result scale, if < 0, calculate the scale according
409: * to the actual values' sizes
410: *
411: * @return A SQLDecimal containing the result of the division
412: *
413: * @exception StandardException Thrown on error
414: */
415:
416: public final NumberDataValue divide(NumberDataValue dividend,
417: NumberDataValue divisor, NumberDataValue result, int scale)
418: throws StandardException {
419: if (result == null) {
420: result = (NumberDataValue) getNewNull();
421: }
422:
423: if (dividend.isNull() || divisor.isNull()) {
424: result.setToNull();
425: return result;
426: }
427:
428: return divideNN(dividend, divisor, result, scale);
429: }
430:
431: public final NumberDataValue minus(NumberDataValue left,
432: NumberDataValue right, NumberDataValue result)
433: throws StandardException {
434: if (result == null) {
435: result = (NumberDataValue) getNewNull();
436: }
437:
438: if (left.isNull() || right.isNull()) {
439: result.setToNull();
440: return result;
441: }
442:
443: return minusNN(left, right, result);
444: }
445:
446: /**
447: * Implement subtraction using addition and negation of the right value.
448: */
449: public NumberDataValue minusNN(NumberDataValue left,
450: NumberDataValue right, NumberDataValue result)
451: throws StandardException {
452: // Requires plusNN() correctly handles that its right argument and
453: // result can be references to the same object.
454: return plusNN(left, right.minus(result), result);
455: }
456:
457: /*
458: ** Abstract methods for handling non-null arithmetic.
459: ** Eventually move these methods into NumberDataType
460: ** and directly compile to them when arguments cannot
461: ** be null. A performance optimization.
462: */
463:
464: /**
465: * Multiple two non-nullable values using DECIMAL arithmetic.
466: */
467: public abstract NumberDataValue timesNN(NumberDataValue left,
468: NumberDataValue right, NumberDataValue result)
469: throws StandardException;
470:
471: /**
472: * Add two non-nullable values using DECIMAL arithmetic.
473: * For subclasses of BinaryDecimal, any implementation
474: * must handle the result and addend2 (right) being references
475: * to the same object.
476: */
477: public abstract NumberDataValue plusNN(NumberDataValue addend1,
478: NumberDataValue addend2, NumberDataValue result)
479: throws StandardException;
480:
481: /**
482: * Divide two non-nullable values using DECIMAL arithmetic.
483: */
484: public abstract NumberDataValue divideNN(NumberDataValue dividend,
485: NumberDataValue divisor, NumberDataValue result, int scale)
486: throws StandardException;
487:
488: /*
489: ** Methods that act directly on twos complement byte arrays.
490: */
491:
492: /**
493: * Compress the passed in byte array so that leading
494: * 0x00 and 0xff are removed when possible.
495: * E.g.
496: * 0x00000453 ->>> 0x0453
497: * 0xfffffff2 ->>> 0xf2
498: * 0xff192312 ->>> 0xff192312 (unchanged)
499: * 0xffff8039 ->>> 0x8039
500: * data2c is set to the compressed value.
501: * @param dataLength Valid length of data in data2c.
502: */
503: private static byte[] reduceBytes2c(byte[] rd, int offset,
504: int dataLength) {
505: // look for leading zeros, if the value
506: // is dataLength bytes long then look
507: // at up to the first (dataLength - 1) bytes
508: // to see if leading 0x00 can be removed.
509:
510: int leading;
511: for (leading = 0; leading < (dataLength - 1); leading++) {
512: if (rd[offset + leading] != (byte) 0)
513: break;
514:
515: // if the hi bit of the next byte is set
516: // then we cannot strip this 0x00 otherwise
517: // the number will turn negative.
518: if ((rd[offset + leading + 1] & 0x80) != 0)
519: break;
520: }
521:
522: if (leading == 0) {
523: // now a similar trick with 0xff, but a slight
524: // complication.
525: for (; leading < (dataLength - 1); leading++) {
526: // Need to check the highest byte of the
527: // would-be remaining significant byte is
528: // set to indicate this is still a negative number
529:
530: if ((rd[offset + leading] == (byte) 0xff)
531: && ((rd[offset + leading + 1] & (byte) 0x80) != 0))
532: continue;
533: break;
534: }
535: }
536:
537: if ((leading != 0) || (rd.length != dataLength)) {
538: byte[] reduced = new byte[dataLength - leading];
539: System.arraycopy(rd, offset + leading, reduced, 0,
540: reduced.length);
541: return reduced;
542: }
543:
544: return rd;
545: }
546:
547: /**
548: * Return the SQL scale of this value, number of digits after the
549: * decimal point, or zero for a whole number.
550: */
551: public int getDecimalValueScale() {
552: if (isNull())
553: return 0;
554:
555: return sqlScale;
556: }
557:
558: /*
559: ** I/O handling
560: */
561:
562: /**
563: * Distill the Decimal to a byte array and
564: * Write out: <UL>
565: * <LI> scale (unsigned byte) </LI>
566: * <LI> length of byte array </LI>
567: * <LI> the byte array </LI> </UL>
568: *
569: */
570: public void writeExternal(ObjectOutput out) throws IOException {
571: // never called when value is null
572: if (SanityManager.DEBUG)
573: SanityManager.ASSERT(!isNull());
574:
575: out.writeByte(sqlScale);
576: out.writeByte(data2c.length);
577: out.write(data2c);
578: }
579:
580: /**
581: * Note the use of data2c: we reuse the array if the
582: * incoming array is the same length or smaller than
583: * the array length.
584: *
585: * @see java.io.Externalizable#readExternal
586: */
587: public void readExternal(ObjectInput in) throws IOException {
588: sqlScale = in.readUnsignedByte();
589: int size = in.readUnsignedByte();
590:
591: /*
592: ** Allocate a new array if the data to read
593: ** is larger than the existing array, or if
594: ** we don't have an array yet.
595:
596: Need to use readFully below and NOT just read because read does not
597: guarantee getting size bytes back, whereas readFully does (unless EOF).
598: */
599: if ((data2c == null) || size != data2c.length) {
600: data2c = new byte[size];
601: }
602: in.readFully(data2c);
603:
604: }
605:
606: public void readExternalFromArray(ArrayInputStream in)
607: throws IOException {
608: sqlScale = in.readUnsignedByte();
609: int size = in.readUnsignedByte();
610:
611: /*
612: ** Allocate a new array if the data to read
613: ** is larger than the existing array, or if
614: ** we don't have an array yet.
615:
616: Need to use readFully below and NOT just read because read does not
617: guarantee getting size bytes back, whereas readFully does (unless EOF).
618: */
619: if ((data2c == null) || size != data2c.length) {
620: data2c = new byte[size];
621: }
622: in.readFully(data2c);
623: }
624:
625: public final int getLength() {
626: return getDecimalValuePrecision();
627: }
628:
629: /* (non-Javadoc)
630: * @see org.apache.derby.iapi.types.DataValueDescriptor#getClone()
631: */
632: public DataValueDescriptor getClone() {
633: BinaryDecimal dvd = (BinaryDecimal) getNewNull();
634:
635: if (this .data2c != null) {
636: dvd.data2c = new byte[data2c.length];
637: System.arraycopy(data2c, 0, dvd.data2c, 0, data2c.length);
638: dvd.sqlScale = sqlScale;
639: }
640:
641: return dvd;
642: }
643:
644: /* (non-Javadoc)
645: * @see org.apache.derby.iapi.types.DataValueDescriptor#setValueFromResultSet(java.sql.ResultSet, int, boolean)
646: */
647: public void setValueFromResultSet(ResultSet resultSet,
648: int colNumber, boolean isNullable)
649: throws StandardException, SQLException {
650: // TODO Auto-generated method stub
651: throw StandardException.newException(SQLState.NOT_IMPLEMENTED);
652:
653: }
654:
655: /* (non-Javadoc)
656: * @see org.apache.derby.iapi.types.DataValueDescriptor#estimateMemoryUsage()
657: */
658: public int estimateMemoryUsage() {
659: // TODO Auto-generated method stub
660: return 0;
661: }
662:
663: public int hashCode() {
664: if (isNull())
665: return 0;
666:
667: try {
668: return (int) getLong();
669: } catch (StandardException se) {
670: return 0;
671: }
672: }
673: }
|