All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.snowflake.client.core.SFJsonResultSet Maven / Gradle / Ivy

There is a newer version: 3.18.0
Show newest version
/*
 * Copyright (c) 2012-2019 Snowflake Computing Inc. All rights reserved.
 */

package net.snowflake.client.core;

import net.snowflake.client.core.arrow.ArrowResultUtil;
import net.snowflake.client.jdbc.ErrorCode;
import net.snowflake.client.jdbc.SnowflakeUtil;
import net.snowflake.client.log.ArgSupplier;
import net.snowflake.client.log.SFLogger;
import net.snowflake.client.log.SFLoggerFactory;
import net.snowflake.common.core.SFBinary;
import net.snowflake.common.core.SFBinaryFormat;
import net.snowflake.common.core.SFTime;
import net.snowflake.common.core.SFTimestamp;
import org.apache.arrow.vector.Float8Vector;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.util.TimeZone;

/**
 * Abstract class used to represent snowflake result set in json format
 */
public abstract class SFJsonResultSet extends SFBaseResultSet
{
  static private final SFLogger logger = SFLoggerFactory.getLogger(
      SFJsonResultSet.class);

  // Timezone used for TimestampNTZ
  private static TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");

  TimeZone timeZone;

  /**
   * Given a column index, get current row's value as an object
   *
   * @param columnIndex
   * @return
   * @throws SFException
   */
  protected abstract Object getObjectInternal(int columnIndex) throws SFException;

  public Object getObject(int columnIndex) throws SFException
  {
    logger.debug(
        "public Object getObject(int columnIndex)");

    int type = resultSetMetaData.getColumnType(columnIndex);

    Object obj = getObjectInternal(columnIndex);
    if (obj == null)
    {
      return null;
    }

    switch (type)
    {
      case Types.VARCHAR:
      case Types.CHAR:
        return getString(columnIndex);

      case Types.BINARY:
        return getBytes(columnIndex);

      case Types.INTEGER:
        return getInt(columnIndex);

      case Types.DECIMAL:
        return getBigDecimal(columnIndex);

      case Types.BIGINT:
        return getLong(columnIndex);

      case Types.DOUBLE:
        return getDouble(columnIndex);

      case Types.TIMESTAMP:
        return getTimestamp(columnIndex);

      case Types.DATE:
        return getDate(columnIndex);

      case Types.TIME:
        return getTime(columnIndex);

      case Types.BOOLEAN:
        return getBoolean(columnIndex);

      default:
        throw (SFException) IncidentUtil.generateIncidentV2WithException(
            session,
            new SFException(ErrorCode.FEATURE_UNSUPPORTED,
                            "data type: " + type),
            null,
            null);
    }
  }

  @Override
  public String getString(int columnIndex) throws SFException
  {
    logger.debug("public String getString(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);
    if (obj == null)
    {
      return null;
    }

    // print timestamp in string format
    int columnType = resultSetMetaData.getInternalColumnType(columnIndex);
    switch (columnType)
    {
      case Types.BOOLEAN:
        return ResultUtil.getBooleanAsString(
            ResultUtil.getBoolean(obj.toString()));

      case Types.TIMESTAMP:
      case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_LTZ:
      case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_TZ:

        SFTimestamp sfTS = getSFTimestamp(columnIndex);
        int columnScale = resultSetMetaData.getScale(columnIndex);

        String timestampStr = ResultUtil.getSFTimestampAsString(
            sfTS, columnType, columnScale, timestampNTZFormatter,
            timestampLTZFormatter, timestampTZFormatter, session);

        logger.debug("Converting timestamp to string from: {} to: {}",
                     (ArgSupplier) obj::toString, timestampStr);

        return timestampStr;

      case Types.DATE:
        Date date = getDate(columnIndex, timeZoneUTC);

        if (dateFormatter == null)
        {
          throw (SFException) IncidentUtil.generateIncidentV2WithException(
              session,
              new SFException(ErrorCode.INTERNAL_ERROR,
                              "missing date formatter"),
              null,
              null);
        }

        String dateStr = ResultUtil.getDateAsString(date, dateFormatter);

        logger.debug("Converting date to string from: {} to: {}",
                     (ArgSupplier) obj::toString, dateStr);

        return dateStr;

      case Types.TIME:
        SFTime sfTime = getSFTime(columnIndex);

        if (timeFormatter == null)
        {
          throw (SFException) IncidentUtil.generateIncidentV2WithException(
              session,
              new SFException(ErrorCode.INTERNAL_ERROR,
                              "missing time formatter"),
              null,
              null);
        }

        int scale = resultSetMetaData.getScale(columnIndex);
        String timeStr = ResultUtil.getSFTimeAsString(sfTime, scale, timeFormatter);

        logger.debug("Converting time to string from: {} to: {}",
                     (ArgSupplier) obj::toString, timeStr);

        return timeStr;

      case Types.BINARY:
        if (binaryFormatter == null)
        {
          throw (SFException) IncidentUtil.generateIncidentV2WithException(
              session,
              new SFException(ErrorCode.INTERNAL_ERROR,
                              "missing binary formatter"),
              null,
              null);
        }

        if (binaryFormatter == SFBinaryFormat.HEX)
        {
          // Shortcut: the values are already passed with hex encoding, so just
          // return the string unchanged rather than constructing an SFBinary.
          return obj.toString();
        }

        SFBinary sfb = new SFBinary(getBytes(columnIndex));
        return binaryFormatter.format(sfb);

      default:
        break;
    }

    return obj.toString();
  }

  @Override
  public boolean getBoolean(int columnIndex) throws SFException
  {
    logger.debug(
        "public boolean getBoolean(int columnIndex)");
    Object obj = getObjectInternal(columnIndex);
    if (obj == null)
    {
      return false;
    }
    if (obj instanceof Boolean)
    {
      return (Boolean) obj;
    }
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    // if type is an approved type that can be converted to Boolean, do this
    if (columnType == Types.BOOLEAN || columnType == Types.INTEGER || columnType == Types.SMALLINT ||
        columnType == Types.TINYINT || columnType == Types.BIGINT || columnType == Types.BIT ||
        columnType == Types.VARCHAR || columnType == Types.CHAR)
    {
      String type = obj.toString();
      if ("1".equals(type) || Boolean.TRUE.toString().equalsIgnoreCase(type))
      {
        return true;
      }
      if ("0".equals(type) || Boolean.FALSE.toString().equalsIgnoreCase(type))
      {
        return false;
      }
    }
    throw new SFException(ErrorCode.INVALID_VALUE_CONVERT,
                          columnType, SnowflakeUtil.BOOLEAN_STR, obj);
  }

  @Override
  public byte getByte(int columnIndex) throws SFException
  {
    logger.debug("public short getByte(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return 0;
    }

    if (obj instanceof String)
    {
      return Byte.parseByte((String) obj);
    }
    else
    {
      return ((Number) obj).byteValue();
    }
  }

  @Override
  public short getShort(int columnIndex) throws SFException
  {
    logger.debug("public short getShort(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return 0;
    }
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    try
    {

      if (obj instanceof String)
      {
        String objString = (String) obj;
        if (objString.contains(".") && (columnType == Types.FLOAT || columnType == Types.DOUBLE))
        {
          objString = objString.substring(0, objString.indexOf("."));
        }
        return Short.parseShort(objString);
      }
      else
      {
        return ((Number) obj).shortValue();
      }
    }
    catch (NumberFormatException ex)
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT,
                            columnType, SnowflakeUtil.SHORT_STR, obj);
    }
  }

  @Override
  public int getInt(int columnIndex) throws SFException
  {
    logger.debug("public int getInt(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return 0;
    }
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    try
    {
      if (obj instanceof String)
      {
        String objString = (String) obj;
        if (objString.contains(".") && (columnType == Types.FLOAT || columnType == Types.DOUBLE))
        {
          objString = objString.substring(0, objString.indexOf("."));
        }
        return Integer.parseInt(objString);
      }
      else
      {
        return ((Number) obj).intValue();
      }
    }
    catch (NumberFormatException ex)
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT,
                            columnType, SnowflakeUtil.INT_STR, obj);
    }

  }


  @Override
  public long getLong(int columnIndex) throws SFException
  {
    logger.debug("public long getLong(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return 0;
    }
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    try
    {
      if (obj instanceof String)
      {
        String objString = (String) obj;
        if (objString.contains(".") && (columnType == Types.FLOAT || columnType == Types.DOUBLE))
        {
          objString = objString.substring(0, objString.indexOf("."));
        }
        return Long.parseLong(objString);
      }
      else
      {
        return ((Number) obj).longValue();
      }
    }
    catch (NumberFormatException nfe)
    {

      if (Types.INTEGER == columnType
          || Types.SMALLINT == columnType)
      {
        throw (SFException) IncidentUtil.generateIncidentV2WithException(
            session,
            new SFException(ErrorCode.INTERNAL_ERROR,
                            SnowflakeUtil.LONG_STR + ": " + obj.toString()),
            null,
            null);
      }
      else
      {
        throw new SFException(ErrorCode.INVALID_VALUE_CONVERT,
                              columnType, SnowflakeUtil.LONG_STR, obj);
      }
    }
  }

  @Override
  public BigDecimal getBigDecimal(int columnIndex) throws SFException
  {
    logger.debug(
        "public BigDecimal getBigDecimal(int columnIndex)");


    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return null;
    }
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    try
    {
      if (columnType != Types.TIME && columnType != Types.TIMESTAMP)
      {
        return new BigDecimal(obj.toString());
      }
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT,
                            columnType, SnowflakeUtil.BIG_DECIMAL_STR, obj);

    }
    catch (NumberFormatException ex)
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT,
                            columnType, SnowflakeUtil.BIG_DECIMAL_STR, obj);
    }

  }

  @Override
  public BigDecimal getBigDecimal(int columnIndex, int scale) throws SFException
  {
    logger.debug(
        "public BigDecimal getBigDecimal(int columnIndex)");


    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return null;
    }

    BigDecimal value = new BigDecimal(obj.toString());

    value = value.setScale(scale, RoundingMode.HALF_UP);

    return value;
  }

  private SFTimestamp getSFTimestamp(int columnIndex) throws SFException
  {
    logger.debug(
        "public Timestamp getTimestamp(int columnIndex)");

    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return null;
    }

    return ResultUtil.getSFTimestamp(
        obj.toString(),
        resultSetMetaData.getScale(columnIndex),
        resultSetMetaData.getInternalColumnType(columnIndex),
        resultVersion, timeZone, session);
  }

  @Override
  public Time getTime(int columnIndex) throws SFException
  {
    logger.debug("public Time getTime(int columnIndex)");

    int columnType = resultSetMetaData.getColumnType(columnIndex);
    if (Types.TIME == columnType)
    {
      SFTime sfTime = getSFTime(columnIndex);
      if (sfTime == null)
      {
        return null;
      }
      return new Time(sfTime.getFractionalSeconds(ResultUtil.DEFAULT_SCALE_OF_SFTIME_FRACTION_SECONDS));
    }
    else if (Types.TIMESTAMP == columnType)
    {
      Timestamp ts = getTimestamp(columnIndex);
      if (ts == null)
      {
        return null;
      }
      return new Time(ts.getTime());
    }
    else
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.TIME_STR,
                            getObjectInternal(columnIndex));
    }
  }

  @Override
  public Timestamp getTimestamp(int columnIndex, TimeZone tz)
  throws SFException
  {
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    if (Types.TIMESTAMP == columnType)
    {
      SFTimestamp sfTS = getSFTimestamp(columnIndex);

      if (sfTS == null)
      {
        return null;
      }

      Timestamp res = sfTS.getTimestamp();

      if (res == null)
      {
        return null;
      }
      // SNOW-14777: for timestamp_ntz, we should treat the time as in client time
      // zone so adjust the timestamp by subtracting the offset of the client
      // timezone
      if (honorClientTZForTimestampNTZ &&
          resultSetMetaData.getInternalColumnType(columnIndex) == Types.TIMESTAMP)
      {
        res = sfTS.moveToTimeZone(tz).getTimestamp();
      }

      Timestamp adjustedTimestamp = ResultUtil.adjustTimestamp(res);

      return adjustedTimestamp;
    }
    else if (Types.DATE == columnType)
    {
      Date d = getDate(columnIndex, tz);
      if (d == null)
      {
        return null;
      }
      return new Timestamp(d.getTime());
    }
    else if (Types.TIME == columnType)
    {
      Time t = getTime(columnIndex);
      if (t == null)
      {
        return null;
      }
      return new Timestamp(t.getTime());
    }
    else
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.TIMESTAMP_STR,
                            getObjectInternal(columnIndex));
    }
  }

  @Override
  public float getFloat(int columnIndex) throws SFException
  {
    logger.debug("public float getFloat(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return 0;
    }

    int columnType = resultSetMetaData.getColumnType(columnIndex);
    try
    {
      if (obj instanceof String)
      {
        if (columnType != Types.TIME && columnType != Types.TIMESTAMP)
        {
          if ("inf".equals(obj))
          {
            return Float.POSITIVE_INFINITY;
          }
          else if ("-inf".equals(obj))
          {
            return Float.NEGATIVE_INFINITY;
          }
          else
          {
            return Float.parseFloat((String) obj);
          }
        }
        throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.FLOAT_STR,
                              getObjectInternal(columnIndex));
      }
      else
      {
        return ((Number) obj).floatValue();
      }
    }
    catch (NumberFormatException ex)
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.FLOAT_STR,
                            getObjectInternal(columnIndex));
    }
  }

  @Override
  public double getDouble(int columnIndex) throws SFException
  {
    logger.debug("public double getDouble(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    // snow-11974: null for getDouble should return 0
    if (obj == null)
    {
      return 0;
    }
    int columnType = resultSetMetaData.getColumnType(columnIndex);
    try
    {
      if (obj instanceof String)
      {
        if (columnType != Types.TIME && columnType != Types.TIMESTAMP)
        {
          if ("inf".equals(obj))
          {
            return Double.POSITIVE_INFINITY;
          }
          else if ("-inf".equals(obj))
          {
            return Double.NEGATIVE_INFINITY;
          }
          else
          {
            return Double.parseDouble((String) obj);
          }
        }
        throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.DOUBLE_STR,
                              getObjectInternal(columnIndex));
      }
      else
      {
        return ((Number) obj).doubleValue();
      }
    }
    catch (NumberFormatException ex)
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.DOUBLE_STR,
                            getObjectInternal(columnIndex));
    }
  }


  @Override
  public byte[] getBytes(int columnIndex) throws SFException
  {
    logger.debug("public byte[] getBytes(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);
    int columnType = resultSetMetaData.getColumnType(columnIndex);

    if (obj == null)
    {
      return null;
    }

    try
    {
      // For all types except time/date/timestamp data, convert data into byte array. Different methods are needed
      // for different types.
      switch (columnType)
      {
        case Types.FLOAT:
        case Types.DOUBLE:
          return ByteBuffer.allocate(Float8Vector.TYPE_WIDTH).putDouble(0, getDouble(columnIndex)).array();
        case Types.NUMERIC:
        case Types.INTEGER:
        case Types.SMALLINT:
        case Types.TINYINT:
        case Types.BIGINT:
          return getBigDecimal(columnIndex).toBigInteger().toByteArray();
        case Types.VARCHAR:
        case Types.CHAR:
          return getString(columnIndex).getBytes();
        case Types.BOOLEAN:
          return getBoolean(columnIndex) ? new byte[]{1} : new byte[]{0};
        case Types.TIMESTAMP:
        case Types.TIME:
        case Types.DATE:
        case Types.DECIMAL:
          throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.BYTES_STR,
                                getObjectInternal(columnIndex));
        default:
          return SFBinary.fromHex(obj.toString()).getBytes();
      }
    }
    catch (IllegalArgumentException ex)
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.BYTES_STR,
                            getObjectInternal(columnIndex));
    }
  }

  @Override
  public Date getDate(int columnIndex) throws SFException
  {
    return getDate(columnIndex, TimeZone.getDefault());
  }

  public Date getDate(int columnIndex, TimeZone tz) throws SFException
  {
    if (tz == null)
    {
      tz = TimeZone.getDefault();
    }

    logger.debug("public Date getDate(int columnIndex)");

    // Column index starts from 1, not 0.
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return null;
    }

    int columnType = resultSetMetaData.getColumnType(columnIndex);

    if (Types.TIMESTAMP == columnType)
    {
      return new Date(getTimestamp(columnIndex, tz).getTime());
    }
    else if (Types.DATE == columnType)
    {
      return ArrowResultUtil.getDate(Integer.parseInt((String) obj));
    }
    // for Types.TIME and all other type, throw user error
    else
    {
      throw new SFException(ErrorCode.INVALID_VALUE_CONVERT, columnType, SnowflakeUtil.DATE_STR, obj);
    }
  }

  private SFTime getSFTime(int columnIndex) throws SFException
  {
    Object obj = getObjectInternal(columnIndex);

    if (obj == null)
    {
      return null;
    }

    int scale = resultSetMetaData.getScale(columnIndex);
    return ResultUtil.getSFTime(obj.toString(), scale, session);
  }

  private Timestamp getTimestamp(int columnIndex) throws SFException
  {
    return getTimestamp(columnIndex, TimeZone.getDefault());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy