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

schemacrawler.crawl.DatabaseInfoRetriever Maven / Gradle / Ivy

Go to download

SchemaCrawler is an open-source Java API that makes working with database metadata as easy as working with plain old Java objects. SchemaCrawler is also a database schema discovery and comprehension, and schema documentation tool. You can search for database schema objects using regular expressions, and output the schema and data in a readable text format. The output is designed to be diff-ed against other database schemas.

There is a newer version: 10.10.05
Show newest version
/* 
 *
 * SchemaCrawler
 * http://sourceforge.net/projects/schemacrawler
 * Copyright (c) 2000-2014, Sualeh Fatehi.
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */

package schemacrawler.crawl;


import static sf.util.DatabaseUtility.executeSql;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import schemacrawler.schema.ColumnDataType;
import schemacrawler.schema.Schema;
import schemacrawler.schema.SchemaReference;
import schemacrawler.schema.SearchableType;
import schemacrawler.schemacrawler.InformationSchemaViews;
import schemacrawler.schemacrawler.SchemaCrawlerException;

final class DatabaseInfoRetriever
  extends AbstractRetriever
{

  private static final Logger LOGGER = Logger
    .getLogger(DatabaseInfoRetriever.class.getName());

  private static final List ignoreMethods = Arrays
    .asList("getDatabaseProductName",
            "getDatabaseProductVersion",
            "getURL",
            "getUserName",
            "getDriverName",
            "getDriverVersion");

  /**
   * Checks if a method is a result set method.
   * 
   * @param method
   *        Method
   * @return Whether a method is a result set method
   */
  private static boolean isDatabasePropertiesResultSetMethod(final Method method)
  {
    final Class returnType = method.getReturnType();
    final boolean isPropertiesResultSetMethod = returnType
      .equals(ResultSet.class) && method.getParameterTypes().length == 0;
    return isPropertiesResultSetMethod;
  }

  /**
   * Checks if a method is a database property.
   * 
   * @param method
   *        Method
   * @return Whether method is a database property
   */
  private static boolean isDatabasePropertyMethod(final Method method)
  {
    final Class returnType = method.getReturnType();
    final boolean notPropertyMethod = returnType.equals(ResultSet.class)
                                      || returnType.equals(Connection.class)
                                      || method.getParameterTypes().length > 0;
    return !notPropertyMethod;
  }

  /**
   * Checks if a method is a database property result set type.
   * 
   * @param method
   *        Method
   * @return Whether a method is a database property result set type
   */
  private static boolean isDatabasePropertyResultSetType(final Method method)
  {
    final String[] databasePropertyResultSetTypes = new String[] {
        "deletesAreDetected",
        "insertsAreDetected",
        "updatesAreDetected",
        "othersDeletesAreVisible",
        "othersInsertsAreVisible",
        "othersUpdatesAreVisible",
        "ownDeletesAreVisible",
        "ownInsertsAreVisible",
        "ownUpdatesAreVisible",
        "supportsResultSetType"
    };
    final boolean isDatabasePropertyResultSetType = Arrays
      .binarySearch(databasePropertyResultSetTypes, method.getName()) >= 0;
    return isDatabasePropertyResultSetType;
  }

  private static ImmutableDatabaseProperty retrieveResultSetTypeProperty(final DatabaseMetaData dbMetaData,
                                                                         final Method method,
                                                                         final int resultSetType,
                                                                         final String resultSetTypeName)
    throws IllegalAccessException, InvocationTargetException
  {
    final String name = method.getName() + "For" + resultSetTypeName
                        + "ResultSets";
    Boolean propertyValue = null;
    propertyValue = (Boolean) method.invoke(dbMetaData,
                                            Integer.valueOf(resultSetType));
    return new ImmutableDatabaseProperty(name, propertyValue);
  }

  DatabaseInfoRetriever(final RetrieverConnection retrieverConnection,
                        final MutableDatabase database)
    throws SQLException
  {
    super(retrieverConnection, database);
  }

  /**
   * Provides additional information on the database.
   * 
   * @throws SQLException
   *         On a SQL exception
   */
  void retrieveAdditionalDatabaseInfo()
    throws SQLException
  {
    final DatabaseMetaData dbMetaData = getMetaData();
    final MutableDatabaseInfo dbInfo = database.getDatabaseInfo();

    final Collection dbProperties = new ArrayList<>();

    final Method[] methods = DatabaseMetaData.class.getMethods();
    for (final Method method: methods)
    {
      try
      {
        if (ignoreMethods.contains(method.getName()))
        {
          continue;
        }
        if (isDatabasePropertyMethod(method))
        {
          if (LOGGER.isLoggable(Level.FINE))
          {
            LOGGER.log(Level.FINER,
                       "Retrieving database property using method: " + method);
          }
          Object value = method.invoke(dbMetaData);
          if (value != null && method.getName().endsWith("s")
              && value instanceof String)
          {
            // Probably a comma-separated list
            value = ((String) value).split(",");
          }
          // Add to the properties map
          dbProperties.add(new ImmutableDatabaseProperty(method.getName(),
                                                         value));
        }
        else if (isDatabasePropertiesResultSetMethod(method))
        {
          if (LOGGER.isLoggable(Level.FINE))
          {
            LOGGER.log(Level.FINER,
                       "Retrieving database property using method: " + method);
          }
          final ResultSet results = (ResultSet) method.invoke(dbMetaData);
          final List resultsList = readResultsVector(results);
          dbProperties
            .add(new ImmutableDatabaseProperty(method.getName(), resultsList
              .toArray(new String[resultsList.size()])));
        }
        else if (isDatabasePropertyResultSetType(method))
        {
          if (LOGGER.isLoggable(Level.FINE))
          {
            LOGGER.log(Level.FINER,
                       "Retrieving database property using method: " + method);
          }
          dbProperties
            .add(retrieveResultSetTypeProperty(dbMetaData,
                                               method,
                                               ResultSet.TYPE_FORWARD_ONLY,
                                               "TYPE_FORWARD_ONLY"));
          dbProperties
            .add(retrieveResultSetTypeProperty(dbMetaData,
                                               method,
                                               ResultSet.TYPE_SCROLL_INSENSITIVE,
                                               "TYPE_SCROLL_INSENSITIVE"));
          dbProperties
            .add(retrieveResultSetTypeProperty(dbMetaData,
                                               method,
                                               ResultSet.TYPE_SCROLL_SENSITIVE,
                                               "TYPE_SCROLL_SENSITIVE"));
        }
      }
      catch (final IllegalAccessException | InvocationTargetException e)
      {
        LOGGER.log(Level.FINE,
                   "Could not execute method, " + method,
                   e.getCause());
      }
      catch (final AbstractMethodError | SQLFeatureNotSupportedException e)
      {
        LOGGER.log(Level.FINE, "JDBC driver does not support " + method, e);
      }
    }

    dbInfo.addAll(dbProperties);

  }

  /**
   * Provides information on the JDBC driver.
   * 
   * @throws SQLException
   *         On a SQL exception
   */
  void retrieveAdditionalJdbcDriverInfo()
    throws SQLException
  {
    final DatabaseMetaData dbMetaData = getMetaData();
    final String url = dbMetaData.getURL();

    final MutableJdbcDriverInfo driverInfo = database.getJdbcDriverInfo();
    if (driverInfo != null)
    {
      try
      {
        final Driver jdbcDriver = DriverManager.getDriver(url);
        final DriverPropertyInfo[] propertyInfo = jdbcDriver
          .getPropertyInfo(url, new Properties());
        for (final DriverPropertyInfo driverPropertyInfo: propertyInfo)
        {
          driverInfo
            .addJdbcDriverProperty(new ImmutableJdbcDriverProperty(driverPropertyInfo));
        }
      }
      catch (final SQLException e)
      {
        LOGGER
          .log(Level.WARNING, "Could not obtain JDBC driver information", e);
      }
    }

  }

  void retrieveAdditionalSchemaCrawlerInfo()
  {
    database.getSchemaCrawlerInfo().setAdditionalSchemaCrawlerInfo();
  }

  /**
   * Provides information on the database.
   * 
   * @throws SQLException
   *         On a SQL exception
   */
  void retrieveDatabaseInfo()
    throws SQLException
  {
    final DatabaseMetaData dbMetaData = getMetaData();

    final MutableDatabaseInfo dbInfo = database.getDatabaseInfo();

    dbInfo.setUserName(dbMetaData.getUserName());
    dbInfo.setProductName(dbMetaData.getDatabaseProductName());
    dbInfo.setProductVersion(dbMetaData.getDatabaseProductVersion());
  }

  /**
   * Provides information on the JDBC driver.
   * 
   * @throws SQLException
   *         On a SQL exception
   */
  void retrieveJdbcDriverInfo()
    throws SQLException
  {
    final DatabaseMetaData dbMetaData = getMetaData();
    final String url = dbMetaData.getURL();

    final MutableJdbcDriverInfo driverInfo = database.getJdbcDriverInfo();
    if (driverInfo != null)
    {
      driverInfo.setDriverName(dbMetaData.getDriverName());
      driverInfo.setDriverVersion(dbMetaData.getDriverVersion());
      driverInfo.setConnectionUrl(url);
      final Driver jdbcDriver = DriverManager.getDriver(url);
      driverInfo.setJdbcDriverClassName(jdbcDriver.getClass().getName());
      driverInfo.setJdbcCompliant(jdbcDriver.jdbcCompliant());
    }

  }

  void retrieveSchemaCrawlerInfo()
  {
    database.getSchemaCrawlerInfo().setSchemaCrawlerInfo();
  }

  /**
   * Retrieves column data type metadata.
   * 
   * @throws SQLException
   *         On a SQL exception
   * @throws SchemaCrawlerException
   */
  void retrieveSystemColumnDataTypes()
    throws SQLException, SchemaCrawlerException
  {
    final Schema systemSchema = new SchemaReference();

    final Statement statement;
    final MetadataResultSet results;

    final InformationSchemaViews informationSchemaViews = getRetrieverConnection()
      .getInformationSchemaViews();
    if (informationSchemaViews.hasOverrideTypeInfoSql())
    {
      final String typeInfoSql = informationSchemaViews
        .getOverrideTypeInfoSql();

      final Connection connection = getDatabaseConnection();
      statement = connection.createStatement();
      results = new MetadataResultSet(executeSql(statement, typeInfoSql));
    }
    else
    {
      statement = null;
      results = new MetadataResultSet(getMetaData().getTypeInfo());
    }

    try
    {
      while (results.next())
      {
        final String typeName = results.getString("TYPE_NAME");
        final int dataType = results.getInt("DATA_TYPE", 0);
        LOGGER.log(Level.FINER, String
          .format("Retrieving data type: %s (with type id %d)",
                  typeName,
                  dataType));
        final long precision = results.getLong("PRECISION", 0L);
        final String literalPrefix = results.getString("LITERAL_PREFIX");
        final String literalSuffix = results.getString("LITERAL_SUFFIX");
        final String createParameters = results.getString("CREATE_PARAMS");
        final boolean isNullable = results
          .getInt("NULLABLE", DatabaseMetaData.typeNullableUnknown) == DatabaseMetaData.typeNullable;
        final boolean isCaseSensitive = results.getBoolean("CASE_SENSITIVE");
        final SearchableType searchable = SearchableType.valueOf(results
          .getInt("SEARCHABLE", SearchableType.unknown.ordinal()));
        final boolean isUnsigned = results.getBoolean("UNSIGNED_ATTRIBUTE");
        final boolean isFixedPrecisionScale = results
          .getBoolean("FIXED_PREC_SCALE");
        final boolean isAutoIncremented = results.getBoolean("AUTO_INCREMENT");
        final String localTypeName = results.getString("LOCAL_TYPE_NAME");
        final int minimumScale = results.getInt("MINIMUM_SCALE", 0);
        final int maximumScale = results.getInt("MAXIMUM_SCALE", 0);
        final int numPrecisionRadix = results.getInt("NUM_PREC_RADIX", 0);

        final MutableColumnDataType columnDataType = lookupOrCreateColumnDataType(systemSchema,
                                                                                  dataType,
                                                                                  typeName);
        // Set the Java SQL type code, but no mapped Java class is
        // available, so use the defaults
        columnDataType.setPrecision(precision);
        columnDataType.setLiteralPrefix(literalPrefix);
        columnDataType.setLiteralSuffix(literalSuffix);
        columnDataType.setCreateParameters(createParameters);
        columnDataType.setNullable(isNullable);
        columnDataType.setCaseSensitive(isCaseSensitive);
        columnDataType.setSearchable(searchable);
        columnDataType.setUnsigned(isUnsigned);
        columnDataType.setFixedPrecisionScale(isFixedPrecisionScale);
        columnDataType.setAutoIncrementable(isAutoIncremented);
        columnDataType.setLocalTypeName(localTypeName);
        columnDataType.setMinimumScale(minimumScale);
        columnDataType.setMaximumScale(maximumScale);
        columnDataType.setNumPrecisionRadix(numPrecisionRadix);

        columnDataType.addAttributes(results.getAttributes());

        database.addColumnDataType(columnDataType);
      }
    }
    finally
    {
      if (results != null)
      {
        results.close();
      }
      if (statement != null)
      {
        statement.close();
      }
    }
  }

  void retrieveUserDefinedColumnDataTypes(final String catalogName,
                                          final String schemaName)
    throws SQLException
  {

    try (final MetadataResultSet results = new MetadataResultSet(getMetaData()
      .getUDTs(catalogName, schemaName, "%", null));)
    {
      while (results.next())
      {
        // "TYPE_CAT", "TYPE_SCHEM"
        final String typeName = results.getString("TYPE_NAME");
        LOGGER.log(Level.FINER, "Retrieving data type: " + typeName);
        final short dataType = results.getShort("DATA_TYPE", (short) 0);
        final String className = results.getString("CLASS_NAME");
        final String remarks = results.getString("REMARKS");
        final short baseTypeValue = results.getShort("BASE_TYPE", (short) 0);

        final Schema schema = new SchemaReference(catalogName, schemaName);
        final ColumnDataType baseType = database
          .lookupColumnDataTypeByType(baseTypeValue);
        final MutableColumnDataType columnDataType = lookupOrCreateColumnDataType(schema,
                                                                                  dataType,
                                                                                  typeName,
                                                                                  className);
        columnDataType.setUserDefined(true);
        columnDataType.setBaseType(baseType);
        columnDataType.setRemarks(remarks);

        columnDataType.addAttributes(results.getAttributes());

        database.addColumnDataType(columnDataType);
      }
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy