/* * 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.tajo.storage.jdbc; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tajo.catalog.*; import org.apache.tajo.catalog.statistics.TableStats; import org.apache.tajo.common.TajoDataTypes.Type; import org.apache.tajo.exception.SQLExceptionUtil; import org.apache.tajo.exception.TajoInternalError; import org.apache.tajo.exception.UndefinedTablespaceException; import org.apache.tajo.exception.UnsupportedDataTypeException; import org.apache.tajo.schema.IdentifierUtil; import org.apache.tajo.util.KeyValueSet; import org.apache.tajo.util.Pair; import javax.annotation.Nullable; import java.net.URI; import java.sql.*; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import static org.apache.tajo.catalog.CatalogUtil.newSimpleDataType; public abstract class JdbcMetadataProviderBase implements MetadataProvider { protected static final Log LOG = LogFactory.getLog(JdbcMetadataProviderBase.class); public static String [] GENERAL_TABLE_TYPES = new String [] {"TABLE"}; protected final JdbcTablespace space; protected final String databaseName; protected final String jdbcUri; protected final Connection connection; public JdbcMetadataProviderBase(JdbcTablespace space, String dbName) { this.space = space; this.databaseName = dbName; ConnectionInfo connInfo = ConnectionInfo.fromURI(space.getUri()); this.jdbcUri = space.getUri().toASCIIString(); try { Class.forName(getJdbcDriverName()).newInstance(); LOG.info(getJdbcDriverName() + " is loaded..."); } catch (Exception e) { throw new TajoInternalError(e); } try { connection = DriverManager.getConnection(jdbcUri, space.connProperties); } catch (SQLException e) { throw new TajoInternalError(e); } } @Override public String getTablespaceName() { return space.getName(); } @Override public URI getTablespaceUri() { return space.getUri(); } @Override public String getDatabaseName() { return databaseName; } @Override public Collection<String> getSchemas() { return Collections.EMPTY_SET; } @Override public Collection<String> getTables(@Nullable String schemaPattern, @Nullable String tablePattern) { ResultSet res = null; List<String> tableNames = Lists.newArrayList(); try { res = connection.getMetaData().getTables(databaseName, schemaPattern, tablePattern, GENERAL_TABLE_TYPES); while(res.next()) { tableNames.add(res.getString("TABLE_NAME")); } } catch (SQLException e) { throw new TajoInternalError(e); } finally { try { if (res != null) { res.close(); } } catch (SQLException e) { LOG.warn(e); } } return tableNames; } private TypeDesc convertDataType(ResultSet res) throws SQLException { final int typeId = res.getInt("DATA_TYPE"); switch (typeId ) { case Types.BOOLEAN: return new TypeDesc(newSimpleDataType(Type.BOOLEAN)); case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: return new TypeDesc(newSimpleDataType(Type.INT4)); case Types.DISTINCT: // sequence for postgresql case Types.BIGINT: return new TypeDesc(newSimpleDataType(Type.INT8)); case Types.FLOAT: return new TypeDesc(newSimpleDataType(Type.FLOAT4)); case Types.NUMERIC: case Types.DECIMAL: case Types.DOUBLE: return new TypeDesc(newSimpleDataType(Type.FLOAT8)); case Types.DATE: return new TypeDesc(newSimpleDataType(Type.DATE)); case Types.TIME: return new TypeDesc(newSimpleDataType(Type.TIME)); case Types.TIMESTAMP: return new TypeDesc(newSimpleDataType(Type.TIMESTAMP)); case Types.CHAR: case Types.NCHAR: case Types.VARCHAR: case Types.NVARCHAR: case Types.CLOB: case Types.NCLOB: case Types.LONGVARCHAR: case Types.LONGNVARCHAR: return new TypeDesc(newSimpleDataType(Type.TEXT)); case Types.BINARY: case Types.VARBINARY: case Types.BLOB: return new TypeDesc(newSimpleDataType(Type.BLOB)); default: throw SQLExceptionUtil.toSQLException(new UnsupportedDataTypeException(typeId + "")); } } @Override public TableDesc getTableDesc(String schemaName, String tableName) throws UndefinedTablespaceException { ResultSet resultForTable = null; ResultSet resultForColumns = null; try { // get table name resultForTable = connection.getMetaData().getTables(databaseName, schemaName, tableName, null); if (!resultForTable.next()) { throw new UndefinedTablespaceException(tableName); } final String name = resultForTable.getString("TABLE_NAME"); // get columns resultForColumns = connection.getMetaData().getColumns(databaseName, schemaName, tableName, null); final List<Pair<Integer, Column>> columns = Lists.newArrayList(); while(resultForColumns.next()) { final int ordinalPos = resultForColumns.getInt("ORDINAL_POSITION"); final String qualifier = resultForColumns.getString("TABLE_NAME"); final String columnName = resultForColumns.getString("COLUMN_NAME"); final TypeDesc type = convertDataType(resultForColumns); final Column c = new Column(IdentifierUtil.buildFQName(databaseName, qualifier, columnName), type); columns.add(new Pair<>(ordinalPos, c)); } // sort columns in an order of ordinal position Collections.sort(columns, new Comparator<Pair<Integer, Column>>() { @Override public int compare(Pair<Integer, Column> o1, Pair<Integer, Column> o2) { return o1.getFirst() - o2.getFirst(); } }); // transform the pair list into collection for columns final Schema schema = SchemaBuilder.builder().addAll(Collections2.transform(columns, new Function<Pair<Integer,Column>, Column>() { @Override public Column apply(@Nullable Pair<Integer, Column> columnPair) { return columnPair.getSecond(); } })).build(); // fill the table stats final TableStats stats = new TableStats(); stats.setNumRows(-1); // unknown TableMeta meta = new TableMeta("rowstore", new KeyValueSet()); final TableDesc table = new TableDesc( IdentifierUtil.buildFQName(databaseName, name), schema, meta, space.getTableUri(meta, databaseName, name) ); table.setStats(stats); return table; } catch (SQLException e) { throw new TajoInternalError(e); } finally { try { if (resultForTable != null) { resultForTable.close(); } if (resultForColumns != null) { resultForColumns.close(); } } catch (SQLException e) { LOG.warn(e); } } } protected abstract String getJdbcDriverName(); }