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

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

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

package net.snowflake.client.core;

import net.snowflake.client.jdbc.ErrorCode;
import net.snowflake.client.jdbc.SnowflakeColumnMetadata;
import net.snowflake.client.jdbc.SnowflakeUtil;
import net.snowflake.common.core.SFTime;
import net.snowflake.common.core.SFTimestamp;
import net.snowflake.common.core.SnowflakeDateTimeFormat;

import java.sql.Date;
import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import net.snowflake.client.log.SFLogger;
import net.snowflake.client.log.SFLoggerFactory;

/**
 * Snowflake ResultSetMetaData
 *
 * @author jhuang
 */
public class SFResultSetMetaData
{
  static final SFLogger logger =
      SFLoggerFactory.getLogger(SFResultSetMetaData.class);

  private int columnCount = 0;

  private List columnNames;

  private List columnTypeNames;

  private List columnTypes;

  private List precisions;

  private List scales;

  private List nullables;

  private List columnSrcTables;

  private List columnSrcSchemas;

  private List columnSrcDatabases;

  private List columnDisplaySizes;

  private String queryId;

  private Map columnNamePositionMap = new HashMap<>();

  private Map columnNameUpperCasePositionMap = new HashMap<>();

  // For creating incidents
  private SFSession session;

  // Date time formatter for calculating the display size
  private SnowflakeDateTimeFormat timestampNTZFormatter;

  private SnowflakeDateTimeFormat timestampLTZFormatter;

  private SnowflakeDateTimeFormat timestampTZFormatter;

  private SnowflakeDateTimeFormat timeFormatter;

  private SnowflakeDateTimeFormat dateFormatter;

  // provide default display size for databasemetadata result set.
  // i.e. result set returned calling getTables etc
  private int timestampNTZStringLength = 30;

  private int timestampLTZStringLength = 30;

  private int timestampTZStringLength = 30;

  private int timeStringLength = 18;

  private int dateStringLength = 10;

  public SFResultSetMetaData(int columnCount,
                             List columnNames,
                             List columnTypeNames,
                             List columnTypes,
                             SFSession session)
  {
    this.columnCount = columnCount;
    this.columnNames = columnNames;
    this.columnTypeNames = columnTypeNames;
    this.columnTypes = columnTypes;
    this.session = session;
  }

  public SFResultSetMetaData(List columnMetadata,
                             SFSession session,
                             SnowflakeDateTimeFormat timestampNTZFormatter,
                             SnowflakeDateTimeFormat timestampLTZFormatter,
                             SnowflakeDateTimeFormat timestampTZFormatter,
                             SnowflakeDateTimeFormat dateFormatter,
                             SnowflakeDateTimeFormat timeFormatter)
  {
    this(columnMetadata, "none", session, timestampNTZFormatter,
          timestampLTZFormatter,
          timestampTZFormatter,
          dateFormatter,
          timeFormatter);
  }

  public SFResultSetMetaData(List columnMetadata,
                             String queryId,
                             SFSession session,
                             SnowflakeDateTimeFormat timestampNTZFormatter,
                             SnowflakeDateTimeFormat timestampLTZFormatter,
                             SnowflakeDateTimeFormat timestampTZFormatter,
                             SnowflakeDateTimeFormat dateFormatter,
                             SnowflakeDateTimeFormat timeFormatter
                             )
  {
    this.columnCount = columnMetadata.size();
    this.queryId = queryId;
    this.timestampNTZFormatter = timestampNTZFormatter;
    this.timestampLTZFormatter = timestampLTZFormatter;
    this.timestampTZFormatter = timestampTZFormatter;
    this.dateFormatter = dateFormatter;
    this.timeFormatter = timeFormatter;
    calculateDateTimeStringLength();

    this.columnNames = new ArrayList(this.columnCount);
    this.columnTypeNames = new ArrayList(this.columnCount);
    this.columnTypes = new ArrayList(this.columnCount);
    this.precisions = new ArrayList(this.columnCount);
    this.scales = new ArrayList(this.columnCount);
    this.nullables = new ArrayList(this.columnCount);
    this.columnSrcDatabases = new ArrayList(this.columnCount);
    this.columnSrcSchemas = new ArrayList(this.columnCount);
    this.columnSrcTables = new ArrayList(this.columnCount);
    this.columnDisplaySizes = new ArrayList(this.columnCount);

    for(int colIdx = 0; colIdx < columnCount; colIdx ++)
    {
      columnNames.add(columnMetadata.get(colIdx).getName());
      columnTypeNames.add(columnMetadata.get(colIdx).getTypeName());
      precisions.add(calculatePrecision(columnMetadata.get(colIdx)));
      columnTypes.add(columnMetadata.get(colIdx).getType());
      scales.add(columnMetadata.get(colIdx).getScale());
      nullables.add(columnMetadata.get(colIdx).isNullable() ?
        ResultSetMetaData.columnNullable : ResultSetMetaData.columnNoNulls);
      columnSrcDatabases.add(columnMetadata.get(colIdx).getColumnSrcDatabase());
      columnSrcSchemas.add(columnMetadata.get(colIdx).getColumnSrcSchema());
      columnSrcTables.add(columnMetadata.get(colIdx).getColumnSrcTable());
      columnDisplaySizes.add(calculateDisplaySize(columnMetadata.get(colIdx)));
    }

    this.session = session;
  }

  private Integer calculatePrecision(SnowflakeColumnMetadata columnMetadata)
  {
    int columnType = columnMetadata.getType();
    switch(columnType)
    {
      case Types.CHAR:
      case Types.VARCHAR:
      case Types.BINARY:
        return columnMetadata.getLength();
      case Types.INTEGER:
      case Types.DECIMAL:
      case Types.BIGINT:
        return columnMetadata.getPrecision();
      case Types.DATE:
        return dateStringLength;
      case Types.TIME:
        return timeStringLength;
      case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_LTZ:
        return timestampLTZStringLength;
      case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_TZ:
        return timestampTZStringLength;
      case Types.TIMESTAMP:
        return timestampNTZStringLength;
      // for double and boolean
      // Precision is not applicable hence return 0
      default:
        return 0;
    }
  }

  private Integer calculateDisplaySize(SnowflakeColumnMetadata columnMetadata)
  {
    int columnType = columnMetadata.getType();
    switch(columnType)
    {
      case Types.CHAR:
      case Types.VARCHAR:
      case Types.BINARY:
        return columnMetadata.getLength();
      case Types.INTEGER:
      case Types.BIGINT:
        // + 1 because number can be negative, it could be -20 for number(2,0)
        return columnMetadata.getPrecision() + 1;
      case Types.DECIMAL:
        // first + 1 because number can be negative, second + 1 because it always
        // include decimal point.
        // i.e. number(2, 1) could be -1.3
        return columnMetadata.getPrecision() + 1 + 1;
      case Types.DOUBLE:
        // Hard code as 24 since the longest float
        // represented in char is
        // -2.2250738585072020E−308
        return 24;
      case Types.DATE:
        return dateStringLength;
      case Types.TIME:
        return timeStringLength;
      case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_LTZ:
        return timestampLTZStringLength;
      case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_TZ:
        return timestampTZStringLength;
      case Types.TIMESTAMP:
        return timestampNTZStringLength;
      case Types.BOOLEAN:
        // Hard code as 5 since the longest char to represent
        // a boolean would be false, which is 5.
        return 5;
      default:
        return 25;
    }
  }

  private void calculateDateTimeStringLength()
  {
    SFTimestamp ts = SFTimestamp.fromMilliseconds(System.currentTimeMillis(), TimeZone.getDefault());
    try
    {
      if (timestampNTZFormatter != null)
      {
        String tsNTZStr = ResultUtil.getSFTimestampAsString(ts, Types.TIMESTAMP, 9, timestampNTZFormatter,
            timestampLTZFormatter, timestampTZFormatter, session);
        timestampNTZStringLength = tsNTZStr.length();
      }
      if (timestampLTZFormatter != null)
      {
        String tsLTZStr = ResultUtil.getSFTimestampAsString(ts, SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_LTZ, 9,
            timestampNTZFormatter, timestampLTZFormatter, timestampTZFormatter, session);
        timestampLTZStringLength = tsLTZStr.length();
      }
      if (timestampTZFormatter != null)
      {
        String tsTZStr = ResultUtil.getSFTimestampAsString(ts, SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_TZ, 9,
            timestampNTZFormatter, timestampLTZFormatter, timestampTZFormatter, session);
        timestampTZStringLength = tsTZStr.length();
      }

      SFTime time = SFTime.fromTimestamp(ts);
      if (timeFormatter != null)
      {
        timeStringLength = ResultUtil.getSFTimeAsString(time, 9, timeFormatter).length();
      }
      if (dateFormatter != null)
      {
        dateStringLength = ResultUtil.getDateAsString(new Date(2015, 11, 11), dateFormatter).length();
      }
    } catch (SFException e)
    {
      logger.warn("Failed to calculate the display size. Use default one.");
    }
  }

  /**
   * get the query id
   * @return query id
   */
  public String getQueryId()
  {
    return queryId;
  }

  /**
   * Get the list of column names
   * @return column names in list
   */
  public List getColumnNames()
  {
    return columnNames;
  }

  /**
   * Get the index of the column by name
   * @param columnName column name
   * @return index of the column that names matches the column name
   */
  public int getColumnIndex(String columnName)
  {
    boolean caseInsensitive = session != null && session.getRsColumnCaseInsensitive();
    columnName = caseInsensitive ? columnName.toUpperCase() : columnName;
    Map nameToIndexMap = caseInsensitive ?
                    columnNameUpperCasePositionMap : columnNamePositionMap;

    if (nameToIndexMap.get(columnName) != null)
    {
      return nameToIndexMap.get(columnName);
    }
    else
    {
      int columnIndex = caseInsensitive ?
          ResultUtil.listSearchCaseInsensitive(columnNames, columnName) :
          columnNames.indexOf(columnName);
      nameToIndexMap.put(columnName, columnIndex);
      return columnIndex;
    }
  }

  /**
   * Get number of columns
   *
   * @return column count
   */
  public int getColumnCount()
  {
    logger.debug("public int getColumnCount()");

    return columnCount;
  }

  public int getColumnType(int column) throws SFException
  {
    logger.debug("public int getColumnType(int column)");

    int internalColumnType = getInternalColumnType(column);

    int externalColumnType = internalColumnType;

    if (internalColumnType == SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_LTZ ||
        internalColumnType == SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_TZ)
      externalColumnType = Types.TIMESTAMP;

      logger.debug("column type = {}", externalColumnType);

    return externalColumnType;
  }

  public int getInternalColumnType(int column) throws SFException
  {
    logger.debug("public int getInternalColumnType(int column)");

    int columnIdx = column-1;
    if (column < 1 || column > columnTypes.size())
    {
      throw new SFException(ErrorCode.COLUMN_DOES_NOT_EXIST, column);
    }

    if(columnTypes.get(columnIdx) == null)
    {
      throw IncidentUtil.generateIncidentWithSignatureAndException(
              session, null, queryId,
              "Missing column type",
              ErrorCode.INTERNAL_ERROR,
              "Missing column type for column " + column);
    }

    return columnTypes.get(columnIdx);
  }

  public String getColumnTypeName(int column) throws SFException
  {
    logger.debug("public String getColumnTypeName(int column)");

    if (column < 1 || column > columnTypeNames.size())
    {
      throw new SFException(ErrorCode.COLUMN_DOES_NOT_EXIST, column);
    }

    if(columnTypeNames == null || columnTypeNames.get(column-1) == null)
    {
      throw
          IncidentUtil.generateIncidentWithSignatureAndException(
                  session, null, queryId,
                  "Missing column type",
                  ErrorCode.INTERNAL_ERROR,
                  "Missing column type for column " + column);
    }

    return columnTypeNames.get(column-1);
  }

  public int getScale(int column)
  {
    logger.debug("public int getScale(int column)");

    if (scales != null && scales.size() >= column)
    {
      return scales.get(column-1);
    }
    else
    {
      // TODO: fix this later to use different defaults for number or timestamp
      return 9;
    }
  }

  public int getPrecision(int column)
  {
    logger.debug("public int getPrecision(int column)");

    if (precisions != null && precisions.size() >= column)
    {
      return precisions.get(column-1);
    }
    else
    {
      // TODO: fix this later to use different defaults for number or timestamp
      return 9;
    }
  }

  public boolean isSigned(int column)
  {
    logger.debug("public boolean isSigned(int column)");

    return (columnTypes.get(column-1) == Types.INTEGER ||
        columnTypes.get(column-1) == Types.DECIMAL ||
        columnTypes.get(column-1) == Types.BIGINT  ||
        columnTypes.get(column-1) == Types.DOUBLE);
  }

  public String getColumnLabel(int column)
  {
    logger.debug("public String getColumnLabel(int column)");

    if(columnNames != null)
      return columnNames.get(column-1);
    else
      return "C" + Integer.toString(column-1);
  }

  public String getColumnName(int column)
  {
    logger.debug("public String getColumnName(int column)");

    if(columnNames != null)
      return columnNames.get(column-1);
    else
      return "C" + Integer.toString(column-1);
  }

  public int isNullable(int column)
  {
    if (nullables != null)
      return nullables.get(column-1);
    else
      return ResultSetMetaData.columnNullableUnknown;
  }

  public String getCatalogName(int column)
  {
    return columnSrcDatabases.get(column-1);
  }

  public String getSchemaName(int column)
  {
    return columnSrcSchemas.get(column-1);
  }

  public String getTableName(int column)
  {
    return columnSrcTables.get(column-1);
  }

  public Integer getColumnDisplaySize(int column)
  {
    return columnDisplaySizes.get(column-1);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy