content: /**
   * Copyright 2010 The Apache Software Foundation
   *
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.wasp.jdbc.value;
  
  import com.alibaba.wasp.DataType;
  import com.alibaba.wasp.SQLErrorCode;
  import com.alibaba.wasp.jdbc.JdbcException;
  import com.alibaba.wasp.util.DateTimeUtils;
  import com.alibaba.wasp.util.StringUtils;
  import com.alibaba.wasp.util.Utils;
  import org.apache.hadoop.hbase.util.Bytes;
  
  import java.io.ByteArrayInputStream;
  import java.io.InputStream;
  import java.math.BigDecimal;
  import java.sql.Date;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Time;
  import java.sql.Timestamp;
  
  /**
   * This is the base class for all value classes. It provides conversion and
   * comparison methods.
   */
  public abstract class Value {
  
    /**
     * The data type is unknown at this time.
     */
    public static final int UNKNOWN = -1;
  
    /**
     * The value type for NULL.
     */
    public static final int NULL = 0;
  
    /**
     * The value type for BOOLEAN values.
     */
    public static final int BOOLEAN = 1;
  
    /**
     * The value type for BYTE values.
     */
    public static final int BYTE = 2;
  
    /**
     * The value type for SHORT values.
     */
    public static final int SHORT = 3;
  
    /**
     * The value type for INT values.
     */
    public static final int INT = 4;
  
    /**
     * The value type for LONG values.
     */
    public static final int LONG = 5;
  
    /**
     * The value type for DECIMAL values.
     */
    public static final int DECIMAL = 6;
  
    /**
     * The value type for DOUBLE values.
     */
    public static final int DOUBLE = 7;
  
    /**
     * The value type for FLOAT values.
     */
    public static final int FLOAT = 8;
  
    /**
     * The value type for INT values.
     */
    public static final int TIME = 9;
  
    /**
     * The value type for DATE values.
     */
    public static final int DATE = 10;
  
    /**
     * The value type for TIMESTAMP values.
     */
    public static final int TIMESTAMP = 11;
  
    /**
     * The value type for BYTES values.
     */
    public static final int BYTES = 12;
  
    /**
     * The value type for STRING values.
     */
    public static final int STRING = 13;
  
    /**
     * The value type for case insensitive STRING values.
     */
    public static final int STRING_IGNORECASE = 14;
  
    /**
     * The value type for BLOB values.
     */
    public static final int BLOB = 15;
  
    /**
     * The value type for CLOB values.
     */
    public static final int CLOB = 16;
  
    /**
     * The value type for ARRAY values.
     */
    public static final int ARRAY = 17;
  
    /**
     * The value type for RESULT_SET values.
     */
    public static final int RESULT_SET = 18;
    /**
     * The value type for JAVA_OBJECT values.
     */
    public static final int JAVA_OBJECT = 19;
  
    /**
     * The value type for UUID values.
     */
    public static final int UUID = 20;
  
    /**
     * The value type for string values with a fixed size.
     */
    public static final int STRING_FIXED = 21;
  
    /**
     * The number of value types.
     */
    public static final int TYPE_COUNT = STRING_FIXED + 1;
  
    /**
     * Get the SQL expression for this value.
     * 
     * @return the SQL expression
     */
    public abstract String getSQL();
  
    /**
     * Get the value type.
     * 
     * @return the type
     */
    public abstract int getType();
  
    /**
     * Get the precision.
     * 
     * @return the precision
     */
    public abstract long getPrecision();
  
    /**
     * Get the value as a string.
     * 
     * @return the string
     */
    public abstract String getString();
  
    /**
     * Get the value as an object.
     * 
     * @return the object
     */
    public abstract Object getObject();
  
    /**
     * Set the value as a parameter in a prepared statement.
     * 
     * @param prep
     *          the prepared statement
     * @param parameterIndex
     *          the parameter index
     */
    public abstract void set(PreparedStatement prep, int parameterIndex)
        throws SQLException;
  
    public abstract int hashCode();
  
    /**
     * Check if the two values have the same hash code. No data conversion is
     * made; this method returns false if the other object is not of the same
     * class. For some values, compareTo may return 0 even if equals return false.
     * Example: ValueDecimal 0.0 and 0.00.
     * 
     * @param other
     *          the other value
     * @return true if they are equal
     */
    public abstract boolean equals(Object other);
  
    /**
     * Get the order of this value type.
     * 
     * @param type
     *          the value type
     * @return the order number
     */
    static int getOrder(int type) {
      switch (type) {
      case UNKNOWN:
        return 1;
      case NULL:
        return 2;
      case STRING:
        return 10;
      case CLOB:
        return 11;
      case STRING_FIXED:
        return 12;
      case STRING_IGNORECASE:
        return 13;
      case BOOLEAN:
        return 20;
      case BYTE:
        return 21;
      case SHORT:
        return 22;
      case INT:
        return 23;
      case LONG:
        return 24;
      case DECIMAL:
        return 25;
      case FLOAT:
        return 26;
      case DOUBLE:
        return 27;
      case TIME:
        return 30;
      case DATE:
        return 31;
      case TIMESTAMP:
        return 32;
      case BYTES:
        return 40;
      case BLOB:
        return 41;
      case UUID:
        return 42;
      case JAVA_OBJECT:
        return 43;
      case ARRAY:
        return 50;
      case RESULT_SET:
        return 51;
      default:
        throw JdbcException.throwInternalError("type:" + type);
      }
    }
  
    /**
     * Get the higher value order type of two value types. If values need to be
     * converted to match the other operands value type, the value with the lower
     * order is converted to the value with the higher order.
     * 
     * @param t1
     *          the first value type
     * @param t2
     *          the second value type
     * @return the higher value type of the two
     */
    public static int getHigherOrder(int t1, int t2) {
      if (t1 == Value.UNKNOWN || t2 == Value.UNKNOWN) {
        if (t1 == t2) {
          throw JdbcException.get(SQLErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
        } else if (t1 == Value.NULL) {
          throw JdbcException.get(SQLErrorCode.UNKNOWN_DATA_TYPE_1, "NULL, ?");
        } else if (t2 == Value.NULL) {
          throw JdbcException.get(SQLErrorCode.UNKNOWN_DATA_TYPE_1, "?, NULL");
        }
      }
      if (t1 == t2) {
        return t1;
      }
      int o1 = getOrder(t1);
      int o2 = getOrder(t2);
      return o1 > o2 ? t1 : t2;
    }
  
    /**
     * Clear the value cache. Used for testing.
     */
    public static void clearCache() {
    }
  
    public Boolean getBoolean() {
      return ((ValueBoolean) convertTo(Value.BOOLEAN)).getBoolean();
    }
  
    public Date getDate() {
      return ((ValueDate) convertTo(Value.DATE)).getDate();
    }
  
    public Time getTime() {
      return ((ValueTime) convertTo(Value.TIME)).getTime();
    }
  
    public Timestamp getTimestamp() {
      return ((ValueTimestamp) convertTo(Value.TIMESTAMP)).getTimestamp();
    }
  
    public byte[] getBytes() {
      return ((ValueBytes) convertTo(Value.BYTES)).getBytes();
    }
  
    public byte[] getBytesNoCopy() {
      return ((ValueBytes) convertTo(Value.BYTES)).getBytesNoCopy();
    }
  
    public byte getByte() {
      return ((ValueByte) convertTo(Value.BYTE)).getByte();
    }
  
    public short getShort() {
      return ((ValueShort) convertTo(Value.SHORT)).getShort();
    }
  
    public BigDecimal getBigDecimal() {
      return ((ValueDecimal) convertTo(Value.DECIMAL)).getBigDecimal();
    }
  
    public double getDouble() {
      return ((ValueDouble) convertTo(Value.DOUBLE)).getDouble();
    }
  
    public float getFloat() {
      return ((ValueFloat) convertTo(Value.FLOAT)).getFloat();
    }
  
    public int getInt() {
      return ((ValueInt) convertTo(Value.INT)).getInt();
    }
  
    public long getLong() {
      return ((ValueLong) convertTo(Value.LONG)).getLong();
    }
  
    public InputStream getInputStream() {
      return new ByteArrayInputStream(getBytesNoCopy());
    }
  
    /**
     * Add a value and return the result.
     * 
     * 
     * 
     * @param v
     *          the value to add
     * @return the result
     */
    public Value add(Value v) {
      throw throwUnsupportedExceptionForType("+");
    }
  
    public int getSignum() {
      throw throwUnsupportedExceptionForType("SIGNUM");
    }
  
    /**
     * Return -value if this value support arithmetic operations.
     * 
     * @return the negative
     */
    public Value negate() {
      throw throwUnsupportedExceptionForType("NEG");
    }
  
    /**
     * Subtract a value and return the result.
     * 
     * @param v
     *          the value to subtract
     * @return the result
     */
    public Value subtract(Value v) {
      throw throwUnsupportedExceptionForType("-");
    }
  
    /**
     * Divide by a value and return the result.
     * 
     * @param v
     *          the value to divide by
     * @return the result
     */
    public Value divide(Value v) {
      throw throwUnsupportedExceptionForType("/");
    }
  
    /**
     * Multiply with a value and return the result.
     * 
     * @param v
     *          the value to multiply with
     * @return the result
     */
    public Value multiply(Value v) {
      throw throwUnsupportedExceptionForType("*");
    }
  
    /**
     * Take the modulus with a value and return the result.
     * 
     * @param v
     *          the value to take the modulus with
     * @return the result
     */
    public Value modulus(Value v) {
      throw throwUnsupportedExceptionForType("%");
    }
  
    /**
     * Compare a value to the specified type.
     * 
     * @param targetType
     *          the type of the returned value
     * @return the converted value
     */
    public Value convertTo(int targetType) {
      // converting NULL is done in ValueNull
      // converting BLOB to CLOB and vice versa is done in ValueLob
      if (getType() == targetType) {
        return this;
      }
      try {
        // decimal conversion
        switch (targetType) {
        case BOOLEAN: {
          switch (getType()) {
          case BYTE:
          case SHORT:
          case INT:
          case LONG:
          case DECIMAL:
          case DOUBLE:
          case FLOAT:
            return ValueBoolean.get(getSignum() != 0);
          case TIME:
          case DATE:
          case TIMESTAMP:
          case BYTES:
          case JAVA_OBJECT:
          case UUID:
            throw JdbcException.get(SQLErrorCode.DATA_CONVERSION_ERROR_1,
                getString());
          }
          break;
        }
        case BYTE: {
          switch (getType()) {
          case BOOLEAN:
            return ValueByte.get(getBoolean().booleanValue() ? (byte) 1
                : (byte) 0);
          case SHORT:
            return ValueByte.get(convertToByte(getShort()));
          case INT:
            return ValueByte.get(convertToByte(getInt()));
          case LONG:
            return ValueByte.get(convertToByte(getLong()));
          case DECIMAL:
            return ValueByte.get(convertToByte(convertToLong(getBigDecimal())));
          case DOUBLE:
            return ValueByte.get(convertToByte(convertToLong(getDouble())));
          case FLOAT:
            return ValueByte.get(convertToByte(convertToLong(getFloat())));
          case BYTES:
            return ValueByte.get((byte) Integer.parseInt(getString(), 16));
          }
          break;
        }
        case SHORT: {
          switch (getType()) {
          case BOOLEAN:
            return ValueShort.get(getBoolean().booleanValue() ? (short) 1
                : (short) 0);
          case BYTE:
            return ValueShort.get(getByte());
          case INT:
            return ValueShort.get(convertToShort(getInt()));
          case LONG:
            return ValueShort.get(convertToShort(getLong()));
          case DECIMAL:
            return ValueShort.get(convertToShort(convertToLong(getBigDecimal())));
          case DOUBLE:
            return ValueShort.get(convertToShort(convertToLong(getDouble())));
          case FLOAT:
            return ValueShort.get(convertToShort(convertToLong(getFloat())));
          case BYTES:
            return ValueShort.get((short) Integer.parseInt(getString(), 16));
          }
          break;
        }
        case INT: {
          switch (getType()) {
          case BOOLEAN:
            return ValueInt.get(getBoolean().booleanValue() ? 1 : 0);
          case BYTE:
            return ValueInt.get(getByte());
          case SHORT:
            return ValueInt.get(getShort());
          case LONG:
            return ValueInt.get(convertToInt(getLong()));
          case DECIMAL:
            return ValueInt.get(convertToInt(convertToLong(getBigDecimal())));
          case DOUBLE:
            return ValueInt.get(convertToInt(convertToLong(getDouble())));
          case FLOAT:
            return ValueInt.get(convertToInt(convertToLong(getFloat())));
          case BYTES:
            return ValueInt.get((int) Long.parseLong(getString(), 16));
          }
          break;
        }
        case LONG: {
          switch (getType()) {
          case BOOLEAN:
            return ValueLong.get(getBoolean().booleanValue() ? 1 : 0);
          case BYTE:
            return ValueLong.get(getByte());
          case SHORT:
            return ValueLong.get(getShort());
          case INT:
            return ValueLong.get(getInt());
          case DECIMAL:
            return ValueLong.get(convertToLong(getBigDecimal()));
          case DOUBLE:
            return ValueLong.get(convertToLong(getDouble()));
          case FLOAT:
            return ValueLong.get(convertToLong(getFloat()));
          case BYTES: {
            // parseLong doesn't work for ffffffffffffffff
            byte[] d = getBytes();
            if (d.length == 8) {
              return ValueLong.get(Utils.readLong(d, 0));
            }
            return ValueLong.get(Long.parseLong(getString(), 16));
          }
          }
          break;
        }
        case DECIMAL: {
          // convert to string is required for JDK 1.4
          switch (getType()) {
          case BOOLEAN:
            return ValueDecimal.get(BigDecimal.valueOf(getBoolean()
                .booleanValue() ? 1 : 0));
          case BYTE:
            return ValueDecimal.get(BigDecimal.valueOf(getByte()));
          case SHORT:
            return ValueDecimal.get(BigDecimal.valueOf(getShort()));
          case INT:
            return ValueDecimal.get(BigDecimal.valueOf(getInt()));
          case LONG:
            return ValueDecimal.get(BigDecimal.valueOf(getLong()));
          case DOUBLE: {
            double d = getDouble();
            if (Double.isInfinite(d) || Double.isNaN(d)) {
              throw JdbcException.get(SQLErrorCode.DATA_CONVERSION_ERROR_1, ""
                  + d);
            }
            return ValueDecimal.get(BigDecimal.valueOf(d));
          }
          case FLOAT: {
            float f = getFloat();
            if (Float.isInfinite(f) || Float.isNaN(f)) {
              throw JdbcException.get(SQLErrorCode.DATA_CONVERSION_ERROR_1, ""
                  + f);
            }
            return ValueDecimal.get(new BigDecimal(Float.toString(f)));
          }
          }
          break;
        }
        case DOUBLE: {
          switch (getType()) {
          case BOOLEAN:
            return ValueDouble.get(getBoolean().booleanValue() ? 1 : 0);
          case BYTE:
            return ValueDouble.get(getByte());
          case SHORT:
            return ValueDouble.get(getShort());
          case INT:
            return ValueDouble.get(getInt());
          case LONG:
            return ValueDouble.get(getLong());
          case DECIMAL:
            return ValueDouble.get(getBigDecimal().doubleValue());
          case FLOAT:
            return ValueDouble.get(getFloat());
          }
          break;
        }
        case FLOAT: {
          switch (getType()) {
          case BOOLEAN:
            return ValueFloat.get(getBoolean().booleanValue() ? 1 : 0);
          case BYTE:
            return ValueFloat.get(getByte());
          case SHORT:
            return ValueFloat.get(getShort());
          case INT:
            return ValueFloat.get(getInt());
          case LONG:
            return ValueFloat.get(getLong());
          case DECIMAL:
            return ValueFloat.get(getBigDecimal().floatValue());
          case DOUBLE:
            return ValueFloat.get((float) getDouble());
          }
          break;
        }
        case DATE: {
          switch (getType()) {
          case TIME:
            // because the time has set the date to 1970-01-01,
            // this will be the result
            return ValueDate.fromDateValue(DateTimeUtils.dateValue(1970, 1, 1));
          case TIMESTAMP:
            return ValueDate
                .fromDateValue(((ValueTimestamp) this).getDateValue());
          }
          break;
        }
        case TIME: {
          switch (getType()) {
          case DATE:
            // need to normalize the year, month and day
            // because a date has the time set to 0, the result will be 0
            return ValueTime.fromNanos(0);
          case TIMESTAMP:
            return ValueTime.fromNanos(((ValueTimestamp) this).getNanos());
          }
          break;
        }
        case TIMESTAMP: {
          switch (getType()) {
          case TIME:
            // return DateTimeUtils.normalizeTimestamp(0,
            // ((ValueTime) this).getNanos());
            return null;
          case DATE:
            return ValueTimestamp.fromDateValueAndNanos(
                ((ValueDate) this).getDateValue(), 0);
          }
          break;
        }
        case BYTES: {
          switch (getType()) {
          case JAVA_OBJECT:
          case BLOB:
            return ValueBytes.getNoCopy(getBytesNoCopy());
          case UUID:
            return ValueBytes.getNoCopy(getBytes());
          case BYTE:
            return ValueBytes.getNoCopy(new byte[]{getByte()});
          case SHORT: {
            int x = getShort();
            return ValueBytes.getNoCopy(new byte[]{(byte) (x >> 8), (byte) x});
          }
          case INT: {
            int x = getInt();
            return ValueBytes.getNoCopy(new byte[]{(byte) (x >> 24),
                (byte) (x >> 16), (byte) (x >> 8), (byte) x});
          }
          case LONG: {
            long x = getLong();
            return ValueBytes.getNoCopy(new byte[]{(byte) (x >> 56),
                (byte) (x >> 48), (byte) (x >> 40), (byte) (x >> 32),
                (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x});
          }
          }
          break;
        }
        case JAVA_OBJECT:
        case BLOB:
        case UUID:
        }
        // conversion by parsing the string value
        String s = getString();
        switch (targetType) {
        case NULL:
          return ValueNull.INSTANCE;
        case BOOLEAN: {
          if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("t")
              || s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("y")) {
            return ValueBoolean.get(true);
          } else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("f")
              || s.equalsIgnoreCase("no") || s.equalsIgnoreCase("n")) {
            return ValueBoolean.get(false);
          } else {
            // convert to a number, and if it is not 0 then it is true
            return ValueBoolean.get(new BigDecimal(s).signum() != 0);
          }
        }
        case BYTE:
          return ValueByte.get(Byte.parseByte(s.trim()));
        case SHORT:
          return ValueShort.get(Short.parseShort(s.trim()));
        case INT:
          return ValueInt.get(Integer.parseInt(s.trim()));
        case LONG:
          return ValueLong.get(Long.parseLong(s.trim()));
        case DECIMAL:
          return ValueDecimal.get(new BigDecimal(s.trim()));
        case TIME:
          return ValueTime.parse(s.trim());
        case DATE:
          return ValueDate.parse(s.trim());
        case TIMESTAMP:
          return ValueTimestamp.parse(s.trim());
        case BYTES:
          return ValueBytes.getNoCopy(StringUtils.convertHexToBytes(s.trim()));
        case JAVA_OBJECT:
        case STRING:
          return ValueString.get(s);
        case STRING_IGNORECASE:
          return ValueStringIgnoreCase.get(s);
        case STRING_FIXED:
          return ValueStringFixed.get(s);
        case DOUBLE:
          return ValueDouble.get(Double.parseDouble(s.trim()));
        case FLOAT:
          return ValueFloat.get(Float.parseFloat(s.trim()));
        case CLOB:
        case BLOB:
        case ARRAY:
          return ValueArray.get(new Value[]{ValueString.get(s)});
        case RESULT_SET:
        case UUID:
        default:
          throw JdbcException.throwInternalError("type=" + targetType);
        }
      } catch (NumberFormatException e) {
        throw JdbcException.get(SQLErrorCode.DATA_CONVERSION_ERROR_1, e,
            getString());
      }
    }
  
    /**
     * Compare this value against another value given that the values are of the
     * same data type.
     * 
     * @param v
     *          the other value
     * @param mode
     *          the compare mode
     * @return 0 if both values are equal, -1 if the other value is smaller, and 1
     *         otherwise
     */
    public final int compareTypeSave(Value v, CompareMode mode) {
      if (this == v) {
        return 0;
      } else if (this == ValueNull.INSTANCE) {
        return -1;
      } else if (v == ValueNull.INSTANCE) {
        return 1;
      }
      return compareSecure(v, mode);
    }
  
    /**
     * Compare this value against another value using the specified compare mode.
     * 
     * @param v
     *          the other value
     * @param mode
     *          the compare mode
     * @return 0 if both values are equal, -1 if the other value is smaller, and 1
     *         otherwise
     */
    public final int compareTo(Value v, CompareMode mode) {
      if (this == v) {
        return 0;
      }
      if (this == ValueNull.INSTANCE) {
        return v == ValueNull.INSTANCE ? 0 : -1;
      } else if (v == ValueNull.INSTANCE) {
        return 1;
      }
      if (getType() == v.getType()) {
        return compareSecure(v, mode);
      }
      int t2 = Value.getHigherOrder(getType(), v.getType());
      return convertTo(t2).compareSecure(v.convertTo(t2), mode);
    }
  
    /**
     * Compare the value with another value of the same type.
     * 
     * @param v
     *          the other value
     * @param mode
     *          the compare mode
     * @return 0 if both values are equal, -1 if the other value is smaller, and 1
     *         otherwise
     */
    protected abstract int compareSecure(Value v, CompareMode mode);
  
    public int getScale() {
      return 0;
    }
  
    /**
     * Convert the scale.
     * 
     * @param onlyToSmallerScale
     *          if the scale should not reduced
     * @param targetScale
     *          the requested scale
     * @return the value
     */
    public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
      return this;
    }
  
    /**
     * Convert the precision to the requested value. The precision of the returned
     * value may be somewhat larger than requested, because values with a fixed
     * precision are not truncated.
     * 
     * @param precision
     *          the new precision
     * @param force
     *          true if losing numeric precision is allowed
     * @return the new value
     */
    public Value convertPrecision(long precision, boolean force) {
      return this;
    }
  
    private static byte convertToByte(long x) {
      if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) {
        throw JdbcException.get(SQLErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1,
            Long.toString(x));
      }
      return (byte) x;
    }
  
    private static short convertToShort(long x) {
      if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) {
        throw JdbcException.get(SQLErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1,
            Long.toString(x));
      }
      return (short) x;
    }
  
    private static int convertToInt(long x) {
      if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) {
        throw JdbcException.get(SQLErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1,
            Long.toString(x));
      }
      return (int) x;
    }
  
    private static long convertToLong(double x) {
      if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) {
        throw JdbcException.get(SQLErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1,
            Double.toString(x));
      }
      return Math.round(x);
    }
  
    private static long convertToLong(BigDecimal x) {
      return 0;
    }
  
    /**
     * Check if this value is linked to a specific table. For values that are kept
     * fully in memory, this method returns false.
     * 
     * @return true if it is
     */
    public boolean isLinked() {
      return false;
    }
  
    /**
     * Mark any underlying resource as 'not linked to any table'. For values that
     * are kept fully in memory this method has no effect.
     */
    public void unlink() {
      // nothing to do
    }
  
    /**
     * Close the underlying resource, if any. For values that are kept fully in
     * memory this method has no effect.
     */
    public void close() {
      // nothing to do
    }
  
    /**
     * Check if the precision is smaller or equal than the given precision.
     * 
     * @param precision
     *          the maximum precision
     * @return true if the precision of this value is smaller or equal to the
     *         given precision
     */
    public boolean checkPrecision(long precision) {
      return getPrecision() <= precision;
    }
  
    /**
     * Get a medium size SQL expression for debugging or tracing. If the precision
     * is too large, only a subset of the value is returned.
     * 
     * @return the SQL expression
     */
    public String getTraceSQL() {
      return getSQL();
    }
  
    public String toString() {
      return getTraceSQL();
    }
  
    /**
     * Throw the exception that the feature is not support for the given data
     * type.
     * 
     * @param op
     *          the operation
     * @return never returns normally
     * @throws com.alibaba.wasp.jdbc.JdbcException
     *           the exception
     */
    protected JdbcException throwUnsupportedExceptionForType(String op) {
      throw JdbcException.getUnsupportedException(DataType.getDataType(getType())
          .name() + " " + op);
    }
  
    /**
     * Get the table (only for LOB object).
     * 
     * @return the table id
     */
    public int getTableId() {
      return 0;
    }
  
    /**
     * Get the byte array.
     * 
     * @return the byte array
     */
    public byte[] getSmall() {
      return null;
    }
  
    /**
     * Copy this value to a temporary file if necessary.
     * 
     * @return the new value
     */
    public Value copyToTemp() {
      return this;
    }
  
    public ResultSet getResultSet() {
      return null;
    }
  
    public static Value toValue(byte[] values, DataType dataType) {
      switch (dataType) {
      case INT32:
        if(values == null || values.length == 0) {
          return ValueInt.ZERO;
        }
        return ValueInt.get(Bytes.toInt(values));
      case INT64:
        if(values == null || values.length == 0) {
          return ValueLong.ZERO;
        } else if (values.length == 4) {
          return ValueInt.get(Bytes.toInt(values));
        } else {
          return ValueLong.get(Bytes.toLong(values));
        }
  
      case DOUBLE:
        if(values == null || values.length == 0) {
          return ValueDouble.ZERO;
        }
        return ValueDouble.get(Bytes.toDouble(values));
      case FLOAT:
        if(values == null || values.length == 0) {
          return ValueFloat.ZERO;
        }
        return ValueFloat.get(Bytes.toFloat(values));
      case STRING:
        return ValueString.get(Bytes.toString(values));
      case DATETIME:
        if(values == null || values.length == 0) {
          return ValueNull.INSTANCE;
        }
        Long ret = Bytes.toLong(values);
        return ValueTimestamp.get(new Timestamp(ret));
      case PROTOBUF:
        // TODO
        return ValueProtobufObject.get(values);
      case UNKNOW:
        return ValueBytes.get(values);
      default:
        throw JdbcException.get(SQLErrorCode.PARSE_ERROR_2,
            "type=" + dataType.name());
      }
  
    }
  
    /**
     * A "binary large object".
     */
    public interface ValueClob {
      // this is a marker interface
    }
  
    /**
     * A "character large object".
     */
    public interface ValueBlob {
      // this is a marker interface
    }
  
  }
  