/** * 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 org.apache.lens.driver.jdbc; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.List; import org.apache.lens.api.query.ResultRow; import org.apache.lens.driver.jdbc.JDBCDriver.QueryResult; import org.apache.lens.server.api.driver.InMemoryResultSet; import org.apache.lens.server.api.driver.LensResultSetMetadata; import org.apache.lens.server.api.error.LensException; import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.serde2.thrift.Type; import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils; import org.apache.hive.service.cli.ColumnDescriptor; import org.apache.hive.service.cli.TypeDescriptor; import org.apache.hive.service.cli.TypeQualifiers; import org.codehaus.jackson.annotate.JsonIgnore; import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** * The Class JDBCResultSet. */ @Slf4j public class JDBCResultSet extends InMemoryResultSet { /** The result meta. */ ResultSetMetaData resultMeta; /** The result set. */ private final ResultSet resultSet; /** The query result. */ private final QueryResult queryResult; /** The lens result meta. */ private LensResultSetMetadata lensResultMeta; /** The close after fetch. */ private final boolean closeAfterFetch; /** * Instantiates a new JDBC result set. * * @param queryResult the query result * @param resultSet the result set * @param closeAfterFetch the close after fetch */ public JDBCResultSet(QueryResult queryResult, ResultSet resultSet, boolean closeAfterFetch) throws LensException { this.queryResult = queryResult; this.resultSet = resultSet; this.closeAfterFetch = closeAfterFetch; } private ResultSetMetaData getRsMetadata() throws LensException { if (resultMeta == null) { try { resultMeta = resultSet.getMetaData(); } catch (SQLException e) { throw new LensException(e); } } return resultMeta; } /* * (non-Javadoc) * * @see org.apache.lens.server.api.driver.LensResultSet#size() */ @Override public Integer size() throws LensException { log.warn("Size of result set is not supported"); return null; } @Override public synchronized LensResultSetMetadata getMetadata() throws LensException { if (lensResultMeta == null) { JDBCResultSetMetadata jdbcResultSetMetadata = new JDBCResultSetMetadata(); jdbcResultSetMetadata.setFieldSchemas(new ArrayList<FieldSchemaData>()); try { ResultSetMetaData rsmeta = getRsMetadata(); for (int i = 1; i <= rsmeta.getColumnCount(); i++) { FieldSchemaData col = new FieldSchemaData(rsmeta.getColumnName(i), TypeInfoUtils.getTypeInfoFromTypeString(getHiveTypeForSQLType(i, rsmeta)).getTypeName(), rsmeta.getColumnTypeName(i)); jdbcResultSetMetadata.getFieldSchemas().add(col); } } catch (Exception e) { log.error("Error getting JDBC type information: {}", e.getMessage(), e); jdbcResultSetMetadata.setFieldSchemas(null); } lensResultMeta = jdbcResultSetMetadata; } return lensResultMeta; } /** * FieldSchemaData is created so that we don't save FieldSchema as some classes used by it don't have * default constructors which are required by jackson */ @Data @NoArgsConstructor public static class FieldSchemaData { private String name; private String type; private String comment; public FieldSchemaData(String name, String type, String comment) { this.name = name; this.type = type; this.comment = comment; } public FieldSchema toFieldSchema() { return new FieldSchema(name, type, comment); } } /** * Result set metadata of a JDBC query */ public static class JDBCResultSetMetadata extends LensResultSetMetadata { @Getter @Setter private List<FieldSchemaData> fieldSchemas; @JsonIgnore @Override public List<ColumnDescriptor> getColumns() { if (fieldSchemas == null) { return null; } List<ColumnDescriptor> columns = new ArrayList<ColumnDescriptor>(fieldSchemas.size()); for (int i = 0; i < fieldSchemas.size(); i++) { FieldSchema schema = fieldSchemas.get(i).toFieldSchema(); columns.add(ColumnDescriptor.newPrimitiveColumnDescriptor(schema.getName(), schema.getComment(), Type.getType(schema.getType()), i + 1)); } return columns; } } /** * Gets the hive type for sql type. * * @param index the index * @param rsmeta the rsmeta * @return the hive type for sql type * @throws SQLException the SQL exception */ public static String getHiveTypeForSQLType(int index, ResultSetMetaData rsmeta) throws SQLException { TypeDescriptor hiveType; TypeQualifiers qualifiers; switch (rsmeta.getColumnType(index)) { case Types.BIGINT: hiveType = new TypeDescriptor(Type.BIGINT_TYPE); break; case Types.TINYINT: case Types.BIT: hiveType = new TypeDescriptor(Type.TINYINT_TYPE); break; case Types.INTEGER: hiveType = new TypeDescriptor(Type.INT_TYPE); break; case Types.SMALLINT: hiveType = new TypeDescriptor(Type.SMALLINT_TYPE); break; case Types.BOOLEAN: hiveType = new TypeDescriptor(Type.BOOLEAN_TYPE); break; case Types.BLOB: case Types.VARBINARY: case Types.JAVA_OBJECT: case Types.LONGVARBINARY: hiveType = new TypeDescriptor(Type.BINARY_TYPE); break; case Types.CHAR: case Types.NCHAR: hiveType = new TypeDescriptor(Type.STRING_TYPE); break; case Types.VARCHAR: case Types.LONGNVARCHAR: case Types.NVARCHAR: hiveType = new TypeDescriptor(Type.STRING_TYPE); break; case Types.NCLOB: case Types.CLOB: case Types.LONGVARCHAR: case Types.DATALINK: case Types.SQLXML: hiveType = new TypeDescriptor(Type.STRING_TYPE); break; case Types.DATE: hiveType = new TypeDescriptor(Type.DATE_TYPE); break; case Types.TIME: case Types.TIMESTAMP: hiveType = new TypeDescriptor(Type.TIMESTAMP_TYPE); break; case Types.FLOAT: hiveType = new TypeDescriptor(Type.TIMESTAMP_TYPE); break; case Types.DECIMAL: hiveType = new TypeDescriptor(Type.DOUBLE_TYPE); break; case Types.DOUBLE: case Types.REAL: case Types.NUMERIC: hiveType = new TypeDescriptor(Type.DOUBLE_TYPE); break; case Types.DISTINCT: case Types.NULL: case Types.OTHER: case Types.REF: case Types.ROWID: hiveType = new TypeDescriptor(Type.USER_DEFINED_TYPE); break; case Types.STRUCT: hiveType = new TypeDescriptor(Type.STRUCT_TYPE); break; case Types.ARRAY: hiveType = new TypeDescriptor(Type.ARRAY_TYPE); break; default: hiveType = new TypeDescriptor(Type.USER_DEFINED_TYPE); break; } return LensResultSetMetadata.getQualifiedTypeName(hiveType); } @Override public void setFetchSize(int size) throws LensException { try { resultSet.setFetchSize(size); } catch (SQLException e) { throw new LensException(e); } } /* * (non-Javadoc) * * @see org.apache.lens.server.api.driver.InMemoryResultSet#next() */ @Override public synchronized ResultRow next() throws LensException { ResultSetMetaData meta = getRsMetadata(); try { List<Object> row = new ArrayList<Object>(meta.getColumnCount()); for (int i = 0; i < meta.getColumnCount(); i++) { row.add(resultSet.getObject(i + 1)); } return new ResultRow(row); } catch (SQLException e) { throw new LensException(e); } } /* * (non-Javadoc) * * @see org.apache.lens.server.api.driver.InMemoryResultSet#hasNext() */ @Override public synchronized boolean hasNext() throws LensException { try { boolean hasMore = resultSet.next(); if (!hasMore && closeAfterFetch) { close(); } return hasMore; } catch (SQLException e) { throw new LensException(e); } } /** * Close. */ public void close() { queryResult.close(); } }