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

org.eobjects.metamodel.jdbc.JdbcDataContext Maven / Gradle / Ivy

/**
 * eobjects.org MetaModel
 * Copyright (C) 2010 eobjects.org
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.eobjects.metamodel.jdbc;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import javax.sql.DataSource;

import org.eobjects.metamodel.AbstractDataContext;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.FirstRowDataSet;
import org.eobjects.metamodel.data.MaxRowsDataSet;
import org.eobjects.metamodel.jdbc.dialects.DB2QueryRewriter;
import org.eobjects.metamodel.jdbc.dialects.DefaultQueryRewriter;
import org.eobjects.metamodel.jdbc.dialects.IQueryRewriter;
import org.eobjects.metamodel.jdbc.dialects.MysqlQueryRewriter;
import org.eobjects.metamodel.jdbc.dialects.PostgresqlQueryRewriter;
import org.eobjects.metamodel.jdbc.dialects.SQLServerQueryRewriter;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.ColumnType;
import org.eobjects.metamodel.schema.MutableColumn;
import org.eobjects.metamodel.schema.MutableRelationship;
import org.eobjects.metamodel.schema.Relationship;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.schema.TableType;
import org.eobjects.metamodel.util.FileHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * DataContextStrategy to use for JDBC-compliant databases
 */
public class JdbcDataContext extends AbstractDataContext implements UpdateableDataContext {

    public static final String DATABASE_PRODUCT_POSTGRESQL = "PostgreSQL";
    public static final String DATABASE_PRODUCT_MYSQL = "MySQL";
    public static final String DATABASE_PRODUCT_HSQLDB = "HSQL Database Engine";
    public static final String DATABASE_PRODUCT_SQLSERVER = "Microsoft SQL Server";
    public static final String DATABASE_PRODUCT_DB2 = "DB2";

    private static final Logger logger = LoggerFactory.getLogger(JdbcDataContext.class);

    private final FetchSizeCalculator _fetchSizeCalculator;
    private final Connection _connection;
    private final DataSource _dataSource;
    private final TableType[] _tableTypes;
    private final String _catalogName;
    private final boolean _singleConnection;

    /**
     * Defines the way that queries are written once dispatched to the database
     */
    private IQueryRewriter _queryRewriter;
    private String _databaseProductName;

    /**
     * There are some confusion as to the definition of catalogs and schemas.
     * Some databases seperate "groups of tables" by using schemas, others by
     * catalogs. This variable indicates whether a MetaModel schema really
     * represents a catalog.
     */
    private boolean _usesCatalogsAsSchemas;
    private String _identifierQuoteString;
    private boolean _supportsBatchUpdates;
    private boolean _isDefaultAutoCommit;

    /**
     * Creates the strategy based on a data source, some table types and an
     * optional catalogName
     * 
     * @param dataSource
     *            the datasource objcet to use for making connections
     * @param tableTypes
     *            the types of tables to include
     * @param catalogName
     *            a catalog name to use, can be null
     */
    public JdbcDataContext(DataSource dataSource, TableType[] tableTypes, String catalogName) {
        this(dataSource, null, tableTypes, catalogName);
    }

    /**
     * Creates the strategy based on a {@link Connection}, some table types and
     * an optional catalogName
     * 
     * @param connection
     *            the database connection
     * @param tableTypes
     *            the types of tables to include
     * @param catalogName
     *            a catalog name to use, can be null
     */
    public JdbcDataContext(Connection connection, TableType[] tableTypes, String catalogName) {
        this(null, connection, tableTypes, catalogName);
    }

    /**
     * Creates the strategy based on a {@link DataSource}, some table types and
     * an optional catalogName
     * 
     * @param dataSource
     *            the data source
     * @param tableTypes
     *            the types of tables to include
     * @param catalogName
     *            a catalog name to use, can be null
     */
    private JdbcDataContext(DataSource dataSource, Connection connection, TableType[] tableTypes, String catalogName) {
        _dataSource = dataSource;
        _connection = connection;
        _tableTypes = tableTypes;
        _catalogName = catalogName;

        if (_dataSource == null) {
            _singleConnection = true;
        } else {
            _singleConnection = false;
        }

        // available memory for fetching is so far fixed at 16 megs.
        _fetchSizeCalculator = new FetchSizeCalculator(16 * 1024 * 1024);
        initialize();
    }

    /**
     * Creates the strategy based on a {@link Connection}
     * 
     * @param connection
     *            the database connection
     */
    public JdbcDataContext(Connection connection) {
        this(connection, TableType.DEFAULT_TABLE_TYPES, null);
    }

    /**
     * Creates the strategy based on a {@link DataSource}
     * 
     * @param dataSource
     *            the data source
     */
    public JdbcDataContext(DataSource dataSource) {
        this(dataSource, TableType.DEFAULT_TABLE_TYPES, null);
    }

    /**
     * Initializes the data context with global metadata about database type and
     * query escaping rules
     */
    private void initialize() {
        Connection connection = getConnection();

        try {
            _isDefaultAutoCommit = connection.getAutoCommit();
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "determine auto-commit behaviour");
        }

        _supportsBatchUpdates = false;
        try {
            DatabaseMetaData metaData = connection.getMetaData();

            _supportsBatchUpdates = supportsBatchUpdates(metaData);

            try {
                _identifierQuoteString = metaData.getIdentifierQuoteString();
                if (_identifierQuoteString != null) {
                    _identifierQuoteString = _identifierQuoteString.trim();
                }
            } catch (SQLException e) {
                logger.warn("could not retrieve identifier quote string from database metadata", e);
            }

            _usesCatalogsAsSchemas = usesCatalogsAsSchemas(metaData);
            try {
                _databaseProductName = metaData.getDatabaseProductName();
                logger.debug("Database product name: {}", _databaseProductName);
                if (DATABASE_PRODUCT_MYSQL.equals(_databaseProductName)) {
                    setQueryRewriter(new MysqlQueryRewriter(this));
                } else if (DATABASE_PRODUCT_POSTGRESQL.equals(_databaseProductName)) {
                    setQueryRewriter(new PostgresqlQueryRewriter(this));
                } else if (DATABASE_PRODUCT_SQLSERVER.equals(_databaseProductName)) {
                    setQueryRewriter(new SQLServerQueryRewriter(this));
                } else if (DATABASE_PRODUCT_DB2.equals(_databaseProductName)) {
                    setQueryRewriter(new DB2QueryRewriter(this));
                } else {
                    setQueryRewriter(new DefaultQueryRewriter(this));
                }

            } catch (SQLException e) {
                logger.warn("Could not retrieve database product name: " + e.getMessage());
            }
        } catch (SQLException e) {
            logger.debug("initialize() threw exception", e);
        } finally {
            closeIfNescesary(connection);
        }
    }

    private boolean supportsBatchUpdates(DatabaseMetaData metaData) {
        try {
            return metaData.supportsBatchUpdates();
        } catch (Exception e) {
            logger.warn("Could not determine if driver support batch updates, returning false", e);
            return false;
        }
    }

    private boolean usesCatalogsAsSchemas(DatabaseMetaData metaData) {
        boolean result = true;
        ResultSet rs = null;
        try {
            rs = metaData.getSchemas();
            while (rs.next() && result) {
                result = false;
            }
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve schema and catalog metadata");
        } finally {
            close(null, rs, null);
        }
        return result;
    }

    protected void loadTables(JdbcSchema schema) {
        Connection connection = getConnection();
        try {
            DatabaseMetaData metaData = connection.getMetaData();

            // Creates string array to represent the table types
            String[] types = getTableTypesAsStrings();
            loadTables(schema, metaData, types);
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve table metadata for " + schema.getName());
        } finally {
            closeIfNescesary(connection);
        }
    }

    private void loadTables(JdbcSchema schema, DatabaseMetaData metaData, String[] types) {
        ResultSet rs = null;
        try {
            if (logger.isInfoEnabled()) {
                logger.info("Querying for table types " + Arrays.toString(types) + " in catalog: " + _catalogName
                        + ", schema: " + schema.getName());
            }
            if (_usesCatalogsAsSchemas) {
                rs = metaData.getTables(schema.getName(), null, null, types);
            } else {
                rs = metaData.getTables(_catalogName, schema.getName(), null, types);
            }
            schema.clearTables();
            while (rs.next()) {
                String tableCatalog = rs.getString(1);
                String tableSchema = rs.getString(2);
                String tableName = rs.getString(3);
                String tableTypeName = rs.getString(4);
                TableType tableType = TableType.getTableType(tableTypeName);
                String tableRemarks = rs.getString(5);

                if (logger.isDebugEnabled()) {
                    logger.debug("Found table: tableCatalog=" + tableCatalog + ",tableSchema=" + tableSchema
                            + ",tableName=" + tableName);
                }

                if (tableSchema == null) {
                    tableSchema = tableCatalog;
                }

                JdbcTable table = new JdbcTable(tableName, tableType, schema, this);
                table.setRemarks(tableRemarks);
                table.setQuote(_identifierQuoteString);
                schema.addTable(table);
            }
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve table metadata for " + schema.getName());
        } finally {
            close(null, rs, null);
        }
    }

    /**
     * Loads index metadata for a table
     * 
     * @param table
     */
    protected void loadIndexes(JdbcTable table) throws MetaModelException {
        Connection connection = getConnection();
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            loadIndexes(table, metaData);
        } catch (Exception e) {
            logger.error("Error loading indexes", e);
            throw new MetaModelException(e);
        } finally {
            closeIfNescesary(connection);
        }
    }

    private void loadIndexes(Table table, DatabaseMetaData metaData) {
        Schema schema = table.getSchema();
        ResultSet rs = null;
        // Ticket #170: IndexInfo is nice-to-have, not need-to-have, so
        // we will do a nice failover on SQLExceptions
        try {
            if (_usesCatalogsAsSchemas) {
                rs = metaData.getIndexInfo(schema.getName(), null, table.getName(), false, true);
            } else {
                rs = metaData.getIndexInfo(_catalogName, schema.getName(), table.getName(), false, true);
            }
            while (rs.next()) {
                String columnName = rs.getString(9);
                if (columnName != null) {
                    MutableColumn column = (MutableColumn) table.getColumnByName(columnName);
                    if (column != null) {
                        column.setIndexed(true);
                    } else {
                        logger.error("Indexed column \"{}\" could not be found in table: {}", columnName, table);
                    }
                }
            }
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve index information for " + table.getName());
        } finally {
            close(null, rs, null);
        }
    }

    /**
     * Loads column metadata (no indexes though) for a table
     * 
     * @param table
     */
    protected void loadColumns(JdbcTable table) {
        Connection connection = getConnection();
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            loadColumns(table, metaData);
        } catch (Exception e) {
            logger.error("Could not load columns for table: " + table, e);
        } finally {
            closeIfNescesary(connection);
        }
    }

    private void loadColumns(JdbcTable table, DatabaseMetaData metaData) {
        Schema schema = table.getSchema();
        ResultSet rs = null;
        try {
            if (logger.isInfoEnabled()) {
                logger.info("Querying for columns in table: " + table.getName());
            }
            int columnNumber = -1;
            if (_usesCatalogsAsSchemas) {
                rs = metaData.getColumns(schema.getName(), null, table.getName(), null);
            } else {
                rs = metaData.getColumns(_catalogName, schema.getName(), table.getName(), null);
            }
            while (rs.next()) {
                columnNumber++;
                String columnName = rs.getString(4);
                if (_identifierQuoteString == null && new StringTokenizer(columnName).countTokens() > 1) {
                    logger.warn("column name contains whitespace: \"" + columnName + "\".");
                }

                int jdbcType = rs.getInt(5);
                ColumnType columnType = ColumnType.convertColumnType(jdbcType);

                String nativeType = rs.getString(6);
                Integer columnSize = rs.getInt(7);

                int jdbcNullable = rs.getInt(11);
                Boolean nullable = null;
                if (jdbcNullable == DatabaseMetaData.columnNullable) {
                    nullable = true;
                } else if (jdbcNullable == DatabaseMetaData.columnNoNulls) {
                    nullable = false;
                }

                String remarks = rs.getString(12);

                JdbcColumn column = new JdbcColumn(columnName, columnType, table, columnNumber, nullable);
                column.setRemarks(remarks);
                column.setNativeType(nativeType);
                column.setColumnSize(columnSize);
                column.setQuote(_identifierQuoteString);
                table.addColumn(column);
            }
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve table metadata for " + table.getName());
        } finally {
            close(null, rs, null);
        }
    }

    protected void loadRelations(Schema schema) {
        Connection connection = getConnection();
        try {
            Table[] tables = schema.getTables();
            DatabaseMetaData metaData = connection.getMetaData();
            for (Table table : tables) {
                loadRelations(table, metaData);
            }
        } catch (Exception e) {
            logger.error("Could not load relations for schema: " + schema, e);
        } finally {
            closeIfNescesary(connection);
        }
    }

    private void loadRelations(Table table, DatabaseMetaData metaData) {
        Schema schema = table.getSchema();
        ResultSet rs = null;
        try {
            if (_usesCatalogsAsSchemas) {
                rs = metaData.getImportedKeys(schema.getName(), null, table.getName());
            } else {
                rs = metaData.getImportedKeys(_catalogName, schema.getName(), table.getName());
            }
            loadRelations(rs, schema);
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve imported keys for " + table.getName());
        } finally {
            close(null, rs, null);
        }
    }

    private void loadRelations(ResultSet rs, Schema schema) throws SQLException {
        while (rs.next()) {
            String pkTableName = rs.getString(3);
            String pkColumnName = rs.getString(4);

            Column pkColumn = null;
            Table pkTable = schema.getTableByName(pkTableName);
            if (pkTable != null) {
                pkColumn = pkTable.getColumnByName(pkColumnName);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Found primary key relation: tableName=" + pkTableName + ",columnName=" + pkColumnName
                        + ", matching column: " + pkColumn);
            }

            String fkTableName = rs.getString(7);
            String fkColumnName = rs.getString(8);
            Column fkColumn = null;
            Table fkTable = schema.getTableByName(fkTableName);
            if (fkTable != null) {
                fkColumn = fkTable.getColumnByName(fkColumnName);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Found foreign key relation: tableName=" + fkTableName + ",columnName=" + fkColumnName
                        + ", matching column: " + fkColumn);
            }

            if (pkColumn == null || fkColumn == null) {
                logger.error(
                        "Could not find relation columns: pkTableName={},pkColumnName={},fkTableName={},fkColumnName={}",
                        new Object[] { pkTableName, pkColumnName, fkTableName, fkColumnName });
                logger.error("pkColumn={}", pkColumn);
                logger.error("fkColumn={}", fkColumn);
            } else {
                Relationship[] relations = pkTable.getRelationships(fkTable);
                boolean exists = false;
                for (Relationship relation : relations) {
                    if (relation.containsColumnPair(pkColumn, fkColumn)) {
                        exists = true;
                        break;
                    }
                }
                if (!exists) {
                    MutableRelationship.createRelationship(new Column[] { pkColumn }, new Column[] { fkColumn });
                }
            }
        }
    }

    public DataSet executeQuery(Query query) throws MetaModelException {
        final Connection connection = getConnection();
        ResultSet resultSet = null;

        boolean postProcessFirstRow = false;
        final Integer firstRow = query.getFirstRow();
        if (firstRow != null) {
            if (_queryRewriter.isFirstRowSupported()) {
                logger.debug("First row property will be treated by query rewriter");
            } else {
                postProcessFirstRow = true;
            }
        }

        final Statement statement;
        try {
            statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "create statement for query");
        }

        boolean postProcessMaxRows = false;
        Integer maxRows = query.getMaxRows();
        if (maxRows != null) {
            if (postProcessFirstRow) {
                // if First row is being post processed, we need to
                // increment the "Max rows" accordingly
                maxRows = maxRows + firstRow;
                query = query.clone().setMaxRows(maxRows);

                logger.info("Setting Max rows to {} because of post processing strategy of First row.", maxRows);
            }

            if (_queryRewriter.isMaxRowsSupported()) {
                logger.debug("Max rows property will be treated by query rewriter");
            } else {
                try {
                    statement.setMaxRows(maxRows);
                } catch (SQLException e) {
                    if (logger.isInfoEnabled()) {
                        logger.info("setMaxRows(" + maxRows + ") was rejected.", e);
                    }
                    postProcessMaxRows = true;
                }
            }
        }

        final String queryString = _queryRewriter.rewriteQuery(query);

        try {
            final int fetchSize = _fetchSizeCalculator.getFetchSize(query);
            logger.info("Applying fetch_size={}", fetchSize);
            try {
                statement.setFetchSize(fetchSize);
            } catch (Exception e) {
                // Ticket #372: Sometimes an exception is thrown here even
                // though it's contrary to the jdbc spec. We'll proceed without
                // doing anything about it though.
                logger.warn("Could not setFetchSize on statement: {}", e.getMessage());
            }

            logger.info("Executing query: {}", queryString);
            resultSet = statement.executeQuery(queryString);

            DataSet dataSet = new JdbcDataSet(query, this, connection, statement, resultSet);

            if (postProcessFirstRow) {
                dataSet = new FirstRowDataSet(dataSet, firstRow);
            }
            if (postProcessMaxRows) {
                dataSet = new MaxRowsDataSet(dataSet, maxRows);
            }
            return dataSet;
        } catch (SQLException e) {
            // only close in case of an error - the JdbcDataSet will close
            // otherwise
            close(connection, resultSet, statement);
            throw JdbcUtils.wrapException(e, "execute query");
        }
    }

    /**
     * Quietly closes any of the parameterized JDBC objects
     * 
     * @param connection
     * 
     * @param rs
     * @param st
     */
    public void close(Connection connection, ResultSet rs, Statement st) {
        closeIfNescesary(connection);
        FileHelper.safeClose(rs, st);
    }

    /**
     * Convenience method to get the available catalogNames using this
     * connection.
     * 
     * @return a String-array with the names of the available catalogs.
     */
    public String[] getCatalogNames() {
        Connection connection = getConnection();

        // Retrieve metadata
        DatabaseMetaData metaData = null;
        ResultSet rs = null;
        try {
            metaData = connection.getMetaData();
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "retrieve metadata");
        }

        // Retrieve catalogs
        logger.debug("Retrieving catalogs");
        List catalogs = new ArrayList();
        try {
            rs = metaData.getCatalogs();
            while (rs.next()) {
                String catalogName = rs.getString(1);
                logger.debug("Found catalogName: " + catalogName);
                catalogs.add(catalogName);
            }
        } catch (SQLException e) {
            logger.error("Error retrieving catalog metadata", e);
        } finally {
            close(connection, rs, null);
            logger.info("Retrieved " + catalogs.size() + " catalogs");
        }
        return catalogs.toArray(new String[catalogs.size()]);
    }

    /**
     * Gets the delegate from the JDBC API (ie. Connection or DataSource) that
     * is being used to perform database interactions.
     * 
     * @return either a DataSource or a Connection, depending on the
     *         configuration of the DataContext.
     */
    public Object getDelegate() {
        if (_dataSource == null) {
            return _connection;
        }
        return _dataSource;
    }

    /**
     * Gets an appropriate connection object to use - either a dedicated
     * connection or a new connection from the datasource object.
     * 
     * Hint: Use the {@link #close(Connection, ResultSet, Statement)} method to
     * close the connection (and any ResultSet or Statements involved).
     */
    public Connection getConnection() {
        if (_dataSource == null) {
            return _connection;
        }
        try {
            return _dataSource.getConnection();
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "establish connection");
        }
    }

    private void closeIfNescesary(Connection con) {
        if (con != null) {
            if (_dataSource != null) {
                // closing connections after individual usage is only nescesary
                // when they are being pulled from a DataSource.
                FileHelper.safeClose(con);
            }
        }
    }

    public String getDefaultSchemaName() {
        // Use a boolean to check if the result has been
        // found, because a schema name can actually be
        // null (for example in the case of Firebird
        // databases).
        boolean found = false;
        String result = null;
        String[] schemaNames = getSchemaNames();

        // First strategy: If there's only one schema available, that must
        // be it
        if (schemaNames.length == 1) {
            result = schemaNames[0];
            found = true;
        }

        if (!found) {
            Connection connection = getConnection();
            try {
                DatabaseMetaData metaData = connection.getMetaData();

                // Second strategy: Find default schema name by examining the
                // URL
                if (!found) {
                    String url = metaData.getURL();
                    if (url != null && url.length() > 0) {
                        if (schemaNames.length > 0) {
                            StringTokenizer st = new StringTokenizer(url, "/\\:");
                            int tokenCount = st.countTokens();
                            if (tokenCount > 0) {
                                for (int i = 1; i < tokenCount; i++) {
                                    st.nextToken();
                                }
                                String lastToken = st.nextToken();

                                for (int i = 0; i < schemaNames.length && !found; i++) {
                                    String schemaName = schemaNames[i];
                                    if (lastToken.indexOf(schemaName) != -1) {
                                        result = schemaName;
                                        found = true;
                                    }
                                }
                            }
                        }
                    }
                }

                // Third strategy: Check for schema equal to username
                if (!found) {
                    String username = metaData.getUserName();
                    if (username != null) {
                        for (int i = 0; i < schemaNames.length && !found; i++) {
                            if (username.equalsIgnoreCase(schemaNames[i])) {
                                result = schemaNames[i];
                                found = true;
                            }
                        }
                    }
                }

            } catch (SQLException e) {
                throw JdbcUtils.wrapException(e, "determine default schema name");
            } finally {
                closeIfNescesary(connection);
            }

            // Fourth strategy: Find default schema name by vendor-specific
            // hacks
            if (!found) {
                if (DATABASE_PRODUCT_POSTGRESQL.equalsIgnoreCase(_databaseProductName)) {
                    if (_catalogName == null) {
                        result = "public";
                    } else {
                        result = _catalogName;
                    }
                    found = true;
                }
                if (DATABASE_PRODUCT_HSQLDB.equalsIgnoreCase(_databaseProductName)) {
                    for (int i = 0; i < schemaNames.length && !found; i++) {
                        String schemaName = schemaNames[i];
                        if ("PUBLIC".equals(schemaName)) {
                            result = schemaName;
                            found = true;
                            break;
                        }
                    }
                }
                if (DATABASE_PRODUCT_SQLSERVER.equals(_databaseProductName)) {
                    for (int i = 0; i < schemaNames.length && !found; i++) {
                        String schemaName = schemaNames[i];
                        if ("dbo".equals(schemaName)) {
                            result = schemaName;
                            found = true;
                            break;
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * Microsoft SQL Server returns users instead of schemas when calling
     * metadata.getSchemas() This is a simple workaround.
     * 
     * @return
     * @throws SQLException
     */
    private Set getSchemaSQLServerNames(DatabaseMetaData metaData) throws SQLException {
        // Distinct schema names. metaData.getTables() is a denormalized
        // resultset
        Set schemas = new HashSet();
        ResultSet rs = metaData.getTables(_catalogName, null, null, getTableTypesAsStrings());
        while (rs.next()) {
            schemas.add(rs.getString("TABLE_SCHEM"));
        }
        return schemas;
    }

    private String[] getTableTypesAsStrings() {
        String[] types = new String[_tableTypes.length];
        for (int i = 0; i < types.length; i++) {
            if (_tableTypes[i] == TableType.OTHER) {
                // if the OTHER type has been selected, don't use a table
                // pattern (ie. include all types)
                types = null;
                break;
            }
            types[i] = _tableTypes[i].toString();
        }
        return types;
    }

    public JdbcDataContext setQueryRewriter(IQueryRewriter queryRewriter) {
        if (queryRewriter == null) {
            throw new IllegalArgumentException("Query rewriter cannot be null");
        }
        _queryRewriter = queryRewriter;
        return this;
    }

    public IQueryRewriter getQueryRewriter() {
        return _queryRewriter;
    }

    public String getIdentifierQuoteString() {
        return _identifierQuoteString;
    }

    @Override
    protected String[] getSchemaNamesInternal() {
        Connection connection = getConnection();
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            Collection result = new ArrayList();

            if (DATABASE_PRODUCT_SQLSERVER.equals(_databaseProductName)) {
                result = getSchemaSQLServerNames(metaData);
            } else if (_usesCatalogsAsSchemas) {
                String[] catalogNames = getCatalogNames();
                for (String name : catalogNames) {
                    logger.info("Found catalogName: {}", name);
                    result.add(name);
                }
            } else {
                ResultSet rs = metaData.getSchemas();
                while (rs.next()) {
                    String schemaName = rs.getString(1);
                    logger.info("Found schemaName: {}", schemaName);
                    result.add(schemaName);
                }
                rs.close();
            }

            if (DATABASE_PRODUCT_MYSQL.equals(_databaseProductName)) {
                result.remove("information_schema");
            }

            // If still no schemas are found, add a schema with a null-name
            if (result.isEmpty()) {
                logger.info("No schemas or catalogs found. Creating unnamed schema.");
                result.add(null);
            }
            return result.toArray(new String[result.size()]);
        } catch (SQLException e) {
            throw JdbcUtils.wrapException(e, "get schema names");
        } finally {
            closeIfNescesary(connection);
        }
    }

    @Override
    protected Schema getSchemaByNameInternal(String name) {
        JdbcSchema schema = new JdbcSchema(name, this);
        loadTables(schema);
        return schema;
    }

    public FetchSizeCalculator getFetchSizeCalculator() {
        return _fetchSizeCalculator;
    }

    @Override
    public void executeUpdate(final UpdateScript update) {
        final JdbcUpdateCallback updateCallback;

        if (_supportsBatchUpdates) {
            updateCallback = new JdbcBatchUpdateCallback(this);
        } else {
            updateCallback = new JdbcSimpleUpdateCallback(this);
        }

        try {
            if (isSingleConnection() && isDefaultAutoCommit()) {
                // if auto-commit is going to be switched off and on during
                // updates, then the update needs to be synchronized, to avoid
                // race-conditions when switching off and on.
                synchronized (_connection) {
                    update.run(updateCallback);
                }
            } else {
                update.run(updateCallback);
            }
            updateCallback.close(true);
        } catch (RuntimeException e) {
            updateCallback.close(false);
            throw e;
        }
    }

    protected boolean isSingleConnection() {
        return _singleConnection;
    }

    protected boolean isDefaultAutoCommit() {
        return _isDefaultAutoCommit;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy