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

com.speedment.runtime.core.internal.db.AbstractDbmsMetadataHandler Maven / Gradle / Ivy

Go to download

A Speedment bundle that shades all dependencies into one jar. This is useful when deploying an application on a server.

The newest version!
/*
 *
 * Copyright (c) 2006-2019, Speedment, Inc. All Rights Reserved.
 *
 * Licensed 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 com.speedment.runtime.core.internal.db;

import com.speedment.common.injector.annotation.ExecuteBefore;
import com.speedment.common.injector.annotation.Inject;
import com.speedment.common.logger.Logger;
import com.speedment.common.logger.LoggerManager;
import com.speedment.runtime.config.*;
import com.speedment.runtime.config.internal.ProjectImpl;
import com.speedment.runtime.config.mutator.ForeignKeyColumnMutator;
import com.speedment.runtime.config.parameter.OrderType;
import com.speedment.runtime.config.trait.HasId;
import com.speedment.runtime.config.trait.HasMainInterface;
import com.speedment.runtime.config.trait.HasName;
import com.speedment.runtime.config.trait.HasParent;
import com.speedment.runtime.config.util.DocumentUtil;
import com.speedment.runtime.core.component.DbmsHandlerComponent;
import com.speedment.runtime.core.component.ProjectComponent;
import com.speedment.runtime.core.component.connectionpool.ConnectionPoolComponent;
import com.speedment.runtime.core.db.*;
import com.speedment.runtime.core.db.metadata.ColumnMetaData;
import com.speedment.runtime.core.db.metadata.TypeInfoMetaData;
import com.speedment.runtime.core.exception.SpeedmentException;
import com.speedment.runtime.core.util.ProgressMeasure;
import com.speedment.runtime.typemapper.TypeMapper;

import java.sql.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

import static com.speedment.common.injector.State.INITIALIZED;
import static com.speedment.common.invariant.NullUtil.requireNonNulls;
import static com.speedment.runtime.core.internal.db.AbstractDbmsOperationHandler.SHOW_METADATA;
import static com.speedment.runtime.core.internal.util.CaseInsensitiveMaps.newCaseInsensitiveMap;
import static com.speedment.runtime.core.util.DatabaseUtil.dbmsTypeOf;
import static java.util.Objects.nonNull;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

/**
 *
 * @author  Emil Forslund
 * @author  Per Minborg
 * @since   3.0.0
 */
public abstract class AbstractDbmsMetadataHandler implements DbmsMetadataHandler {
    
    private final static Logger LOGGER = LoggerManager.getLogger(AbstractDbmsMetadataHandler.class);
    private final static Class DEFAULT_MAPPING = Object.class;
    
    private @Inject ConnectionPoolComponent connectionPoolComponent;
    private @Inject DbmsHandlerComponent dbmsHandlerComponent;
    private @Inject ProjectComponent projectComponent;
    private JavaTypeMap javaTypeMap;

    private Map, AtomicLong> timers;

    protected AbstractDbmsMetadataHandler() {
        timers = new ConcurrentHashMap<>();
    }
    
    @ExecuteBefore(INITIALIZED)
    final void createJavaTypeMap() {
        this.javaTypeMap = newJavaTypeMap();
    }
    
    protected JavaTypeMap newJavaTypeMap() {
        return JavaTypeMap.create();
    }
    
    @Override
    public CompletableFuture readSchemaMetadata(
            final Dbms dbms,
            final ProgressMeasure progress,
            final Predicate filterCriteria) {

        requireNonNulls(filterCriteria, progress);

        // Create a deep copy of the project document.
        final Project projectCopy = DocumentUtil.deepCopy(
            projectComponent.getProject(), ProjectImpl::new
        );

        // Make sure there are not multiple dbmses with the same id
        final Set ids = new HashSet<>();
        if (!projectCopy.dbmses().map(Dbms::getId).allMatch(ids::add)) {
            final Set duplicates = new HashSet<>();
            ids.clear();

            projectCopy.dbmses()
                .map(Dbms::getId)
                .forEach(s -> {
                    if (!ids.add(s)) {
                        duplicates.add(s);
                    }
                });

            throw new SpeedmentException(
                "The following dbmses have duplicates in the config document: "
                + duplicates
            );
        }

        // Locate the dbms in the copy.
        final Dbms dbmsCopy = projectCopy.dbmses()
            .filter(d -> d.getId().equals(dbms.getId()))
            .findAny().orElseThrow(() -> new SpeedmentException(
                "Could not find Dbms document in copy."
            ));

        return readSchemaMetadata(
            projectCopy, dbmsCopy, filterCriteria, progress
        ).whenCompleteAsync((project, ex) -> {
            progress.setProgress(ProgressMeasure.DONE);
            if (ex != null) {
                progress.setCurrentAction("Error!");
                throw new SpeedmentException("Unable to read schema metadata.", ex);
            } else {
                progress.setCurrentAction("Done!");
            }
            LOGGER.info("Aggregate duration of metadata retrieval [ms]: " +
                timers.entrySet().stream()
                    .map(e -> String.format("%s=%,d", e.getKey().getSimpleName(), e.getValue().get()))
                .collect(joining(", "))
            );
        });
    }
    
    
    @Override
    public String getDbmsInfoString(Dbms dbms) throws SQLException {
        try (final Connection conn = getConnection(dbms)) {
            final DatabaseMetaData md = conn.getMetaData();
            return md.getDatabaseProductName() +
                ", " +
                md.getDatabaseProductVersion() +
                ", " +
                md.getDriverName() +
                " " +
                md.getDriverVersion() +
                ", JDBC version " +
                md.getJDBCMajorVersion() +
                "." +
                md.getJDBCMinorVersion();
        }
    }        

    private CompletableFuture readSchemaMetadata(
        final Project project,
        final Dbms dbms,
        final Predicate filterCriteria,
        final ProgressMeasure progress
    ) {
        requireNonNulls(project, dbms, filterCriteria, progress);

        final DbmsType dbmsType = dbmsTypeOf(dbmsHandlerComponent, dbms);
        final String action = actionName(dbms);

        LOGGER.info(action);
        progress.setCurrentAction(action);

        final Set discardedSchemas = new HashSet<>();
        final DatabaseNamingConvention naming = dbmsType.getDatabaseNamingConvention();
        final Set preSet = dbmsType.getDataTypes();

        // Task that downloads the SQL Type Mappings from the database
        final CompletableFuture>> sqlTypeMappingTask
            = CompletableFuture.supplyAsync(() -> {
                final Map> sqlTypeMapping;

                try {
                    sqlTypeMapping = !preSet.isEmpty()
                        ? readTypeMapFromSet(preSet)
                        : readTypeMapFromDB(dbms);
                } catch (final SQLException ex) {
                    throw new SpeedmentException(
                        "Error loading type map from database.", ex
                    );
                }

                return sqlTypeMapping;
            });

        // Task that downloads the schemas from the database
        final CompletableFuture schemasTask = CompletableFuture.runAsync(() -> {
            try (final Connection connection = getConnection(dbms)) {
                try (final ResultSet rs = connection.getMetaData().getSchemas(null, null)) {
                    while (rs.next()) {

                        final String name = readSchemaName(rs, dbmsType);

                        boolean schemaWasUsed = false;
                        if (!naming.getSchemaExcludeSet().contains(name)) {
                            if (filterCriteria.test(name)) {
                                final Schema schema = dbms.mutator().addNewSchema();                                
                                schema.mutator().setId(name);
                                schema.mutator().setName(name);
                                schemaWasUsed = true;
                            }
                        }

                        if (!schemaWasUsed) {
                            discardedSchemas.add(name);
                        }
                    }
                }
            } catch (final SQLException sqle) {
                throw new SpeedmentException(
                    "Error reading metadata from result set.", sqle
                );
            }
        });

        // Task that downloads the catalogs from the database
        final CompletableFuture catalogsTask = CompletableFuture.runAsync(() -> {
            try (final Connection connection = getConnection(dbms)) {
                try (final ResultSet catalogResultSet = connection.getMetaData().getCatalogs()) {
                    while (catalogResultSet.next()) {
                        final String schemaName = catalogResultSet.getString(1);

                        boolean schemaWasUsed = false;
                        if (filterCriteria.test(schemaName)) {
                            if (!naming.getSchemaExcludeSet().contains(schemaName)) {
                                final Schema schema = dbms.mutator().addNewSchema();
                                schema.mutator().setId(schemaName);
                                schema.mutator().setName(schemaName);
                                schemaWasUsed = true;
                            }
                        }

                        if (!schemaWasUsed) {
                            discardedSchemas.add(schemaName);
                        }
                    }
                }
            } catch (final SQLException sqle) {
                throw new SpeedmentException(
                    "Error reading metadata from result set.", sqle
                );
            }
        });

        // Create a new task that will execute once the schemas and the catalogs 
        // have been loaded independently of each other.
        return CompletableFuture.allOf(
            schemasTask,
            catalogsTask
        ).thenComposeAsync(v -> {
            @SuppressWarnings({"unchecked", "rawtypes"})
            final CompletableFuture[] tablesTask
                = dbms.schemas()
                .map(schema -> tables(sqlTypeMappingTask, dbms, schema, progress))
                .toArray(s -> (CompletableFuture[]) new CompletableFuture[s]);

            return CompletableFuture.allOf(tablesTask)
                .handleAsync((v2, ex) -> {
                    if (ex == null) {
                        if (tablesTask.length == 0) {
                            throw new SpeedmentException(
                                "Could not find any matching schema. The following schemas was considered: " + discardedSchemas + "."
                            );
                        } else {
                            return project;
                        }
                    } else {
                        throw new SpeedmentException(
                            "An exception occured while the tables were loading.", ex
                        );
                    }
                });
        });
    }

    private String readSchemaName(ResultSet rs, DbmsType dbmsType) throws SQLException {
        final String schemaName = rs.getString(dbmsType.getResultSetTableSchema());
        String catalogName = "";
        
        try {
            // This column is not there for Oracle so handle it
            // gracefully....
            catalogName = rs.getString("TABLE_CATALOG");
        } catch (final SQLException ex) {
            LOGGER.info("TABLE_CATALOG not in result set.");
        }
        
        return Optional.ofNullable(schemaName).orElse(catalogName);
    }

    protected CompletableFuture tables(CompletableFuture>> sqlTypeMapping, Dbms dbms, Schema schema, ProgressMeasure progressListener) {
        requireNonNulls(sqlTypeMapping, dbms, schema, progressListener);
        
        // If the wrapped task has already been cancelled, there is no point in going on.
        if (sqlTypeMapping.isCancelled()) {
            return CompletableFuture.completedFuture(null);
        }

        final String action = actionName(schema);
        LOGGER.info(action);
        progressListener.setCurrentAction(action);

        final long begin = System.currentTimeMillis();
        try (final Connection connection = getConnection(dbms)) {
            try (final ResultSet rsTable = connection.getMetaData().getTables(
                    jdbcCatalogLookupName(schema),
                    jdbcSchemaLookupName(schema),
                    null, new String[] {"TABLE", "VIEW"})) {

                if (SHOW_METADATA) {
                    final ResultSetMetaData rsmd = rsTable.getMetaData();
                    int numberOfColumns = rsmd.getColumnCount();
                    for (int x = 1; x <= numberOfColumns; x++) {
                        LOGGER.debug(rsmd.getColumnName(x) + ", " + rsmd.getColumnClassName(x) + ", " + rsmd.getColumnType(x));
                    }
                }

                while (rsTable.next()) {
                    if (SHOW_METADATA) {
                        final ResultSetMetaData rsmd = rsTable.getMetaData();
                        int numberOfColumns = rsmd.getColumnCount();
                        for (int x = 1; x <= numberOfColumns; x++) {
                            LOGGER.debug(rsmd.getColumnName(x) + ":'" + rsTable.getObject(x) + "'");
                        }
                    }
                    
                    final Table table      = schema.mutator().addNewTable();
                    final String tableName = rsTable.getString("TABLE_NAME");
                    final String tableType = rsTable.getString("TABLE_TYPE");
                    table.mutator().setId(tableName);
                    table.mutator().setName(tableName);
                    table.mutator().setView("VIEW".equals(tableType));
                }
            }
        } catch (final SQLException sqle) {
            throw new SpeedmentException(sqle);
        }
        final long duration = System.currentTimeMillis() - begin;
        timers.computeIfAbsent(Table.class, $ -> new AtomicLong()).addAndGet(duration);

        final AtomicInteger cnt = new AtomicInteger();
        final double noTables = schema.tables().count();

        return CompletableFuture.allOf(
            schema.tables().map(table -> sqlTypeMapping.thenAcceptAsync(mapping -> {
                try (final Connection connection = getConnection(dbms)) {
                    progressListener.setCurrentAction(actionName(table));
                    columns(connection, mapping, table, progressListener);
                    indexes(connection, table, progressListener);
                    foreignKeys(connection, table, progressListener);
                    primaryKeyColumns(connection, table, progressListener);
                    progressListener.setProgress(cnt.incrementAndGet() / noTables);
                } catch (final SQLException ex) {
                    throw new SpeedmentException(ex);
                }
            })).toArray(CompletableFuture[]::new)
        ).thenApplyAsync(v -> schema);
    }

    protected void columns(
        final Connection connection, 
        final Map> sqlTypeMapping, 
        final Table table, 
        final ProgressMeasure progressListener
    ) {
        requireNonNulls(connection, table);

        final Schema schema = table.getParentOrThrow();

        final SqlSupplier supplier = ()
            -> connection.getMetaData().getColumns(
                jdbcCatalogLookupName(schema),
                jdbcSchemaLookupName(schema),
                metaDataTableNameForColumns(table),
                null
            );

        final AbstractDbmsOperationHandler.TableChildMutator mutator = (column, rs) -> {

            final ColumnMetaData md = ColumnMetaData.of(rs);

            final String columnName = md.getColumnName();
            
            column.mutator().setId(columnName);
            column.mutator().setName(columnName);
            column.mutator().setOrdinalPosition(md.getOrdinalPosition());

            final boolean nullable;
            final int nullableValue = md.getNullable();
            switch (nullableValue) {
                case DatabaseMetaData.columnNullable:
                case DatabaseMetaData.columnNullableUnknown: {
                    nullable = true;
                    break;
                }
                case DatabaseMetaData.columnNoNulls: {
                    nullable = false;
                    break;
                }
                default:
                    throw new SpeedmentException("Unknown nullable type " + nullableValue);
            }

            column.mutator().setNullable(nullable);

            final Class lookupJdbcClass = javaTypeMap.findJdbcType(sqlTypeMapping, md);

            final Class selectedJdbcClass;
            if (lookupJdbcClass != null) {
                selectedJdbcClass = lookupJdbcClass;
            } else {
                // Fall-back to DEFAULT_MAPPING
                selectedJdbcClass = DEFAULT_MAPPING;
                LOGGER.warn(
                    String.format("Unable to determine mapping for table %s, column %s. "
                        + "Type name %s, data type %d, decimal digits %d."
                        + "Fallback to JDBC-type %s",
                        table.getId(),
                        column.getId(),
                        md.getTypeName(),
                        md.getDataType(),
                        md.getDecimalDigits(),
                        selectedJdbcClass.getSimpleName()
                    )
                );                    
            }

            column.mutator().setDatabaseType(selectedJdbcClass);
                       
//            column.mutator().setTypeMapper(TypeMapper.identity());            
            if (!nullable) {
                if (selectedJdbcClass == Byte.class
                ||  selectedJdbcClass == Short.class
                ||  selectedJdbcClass == Integer.class
                ||  selectedJdbcClass == Long.class
                ||  selectedJdbcClass == Float.class
                ||  selectedJdbcClass == Double.class
                ||  selectedJdbcClass == Character.class
                ||  selectedJdbcClass == Boolean.class) {
                    column.mutator().setTypeMapper(TypeMapper.primitive().getClass());
                }
            }
            
            if ("ENUM".equalsIgnoreCase(md.getTypeName())) {
                final Dbms dbms = schema.getParentOrThrow();
                final List constants = enumConstantsOf(dbms, table, columnName);
                column.mutator().setEnumConstants(constants.stream().collect(joining(",")));
            }

            setAutoIncrement(column, md);
            progressListener.setCurrentAction(actionName(column));

        };

        tableChilds(Column.class, table.mutator()::addNewColumn, supplier, mutator, progressListener);
    }

    protected void primaryKeyColumns(Connection connection, Table table, ProgressMeasure progressListener) {
        requireNonNulls(connection, table);

        final Schema schema = table.getParentOrThrow();

        final SqlSupplier supplier = ()
            -> connection.getMetaData().getPrimaryKeys(jdbcCatalogLookupName(schema),
                jdbcSchemaLookupName(schema),
                metaDataTableNameForPrimaryKeys(table)
            );

        final AbstractDbmsOperationHandler.TableChildMutator mutator = (primaryKeyColumn, rs) -> {
            final String columnName = rs.getString("COLUMN_NAME");
            primaryKeyColumn.mutator().setId(columnName);
            primaryKeyColumn.mutator().setName(columnName);
            primaryKeyColumn.mutator().setOrdinalPosition(rs.getInt("KEY_SEQ"));
        };

        tableChilds(PrimaryKeyColumn.class, table.mutator()::addNewPrimaryKeyColumn, supplier, mutator, progressListener);
        
        if (!table.isView() && table.primaryKeyColumns().noneMatch(pk -> true)) {
            LOGGER.warn("Table '" + table.getId() + "' does not have any primary key.");
        }
    }

    protected void indexes(Connection connection, Table table, ProgressMeasure progressListener) {
        requireNonNulls(connection, table);

        // Fix #566: Some connectors throw an exception if getIndexInfo() is
        // invoked for a database VIEW.
        if (table.isView()) {
            return;
        }

        final Schema schema = table.getParentOrThrow();
        final SqlSupplier supplier = ()
            -> connection.getMetaData().getIndexInfo(
                jdbcCatalogLookupName(schema),
                jdbcSchemaLookupName(schema),
                metaDataTableNameForIndexes(table), // Todo: break out in protected method
                false,
                // 'true' below might speed up metadata retrieval since approximations can be used
                // See https://github.com/speedment/speedment-enterprise/issues/168
                true 
            );

        final AbstractDbmsOperationHandler.TableChildMutator mutator = (index, rs) -> {
            final String indexName = rs.getString("INDEX_NAME");
            final boolean unique = !rs.getBoolean("NON_UNIQUE");

            index.mutator().setId(indexName);
            index.mutator().setName(indexName);
            index.mutator().setUnique(unique);

            final IndexColumn indexColumn = index.mutator().addNewIndexColumn();
            final String columnName = rs.getString("COLUMN_NAME");
            indexColumn.mutator().setId(columnName);
            indexColumn.mutator().setName(columnName);
            indexColumn.mutator().setOrdinalPosition(rs.getInt("ORDINAL_POSITION"));
            final String ascOrDesc = rs.getString("ASC_OR_DESC");

            if ("A".equalsIgnoreCase(ascOrDesc)) {
                indexColumn.mutator().setOrderType(OrderType.ASC);
            } else if ("D".equalsIgnoreCase(ascOrDesc)) {
                indexColumn.mutator().setOrderType(OrderType.DESC);
            } else {
                indexColumn.mutator().setOrderType(OrderType.NONE);
            }
        };

        final SqlPredicate filter = rs -> {
            final String type = rs.getString("TYPE");
            final String indexName = rs.getString("INDEX_NAME");
            return nonNull(indexName);
        };

        tableChilds(Index.class, table.mutator()::addNewIndex, supplier, mutator, filter, progressListener);
    }

    protected void foreignKeys(Connection connection, Table table, ProgressMeasure progressListener) {
        requireNonNulls(connection, table);

        final Schema schema = table.getParentOrThrow();
        final SqlSupplier supplier = ()
            -> connection.getMetaData().getImportedKeys(
                jdbcCatalogLookupName(schema),
                jdbcSchemaLookupName(schema),
                metaDataTableNameForForeignKeys(table)
            );

        final AbstractDbmsOperationHandler.TableChildMutator mutator = (foreignKey, rs) -> {

            final String foreignKeyName = rs.getString("FK_NAME");
            foreignKey.mutator().setId(foreignKeyName);
            foreignKey.mutator().setName(foreignKeyName);

            final ForeignKeyColumn foreignKeyColumn = foreignKey.mutator().addNewForeignKeyColumn();
            final ForeignKeyColumnMutator fkcMutator = foreignKeyColumn.mutator();
            final String fkColumnName = rs.getString("FKCOLUMN_NAME");
            fkcMutator.setId(fkColumnName);
            fkcMutator.setName(fkColumnName);
            fkcMutator.setOrdinalPosition(rs.getInt("KEY_SEQ"));
            fkcMutator.setForeignTableName(rs.getString("PKTABLE_NAME"));
            fkcMutator.setForeignColumnName(rs.getString("PKCOLUMN_NAME"));

            // FKs always point to the same DBMS but can
            // be changed to another one using the config 
            fkcMutator.setForeignDatabaseName(schema.getParentOrThrow().getId());

            // Use schema name first but if not present, use catalog name
            fkcMutator.setForeignSchemaName(
                Optional.ofNullable(rs.getString("FKTABLE_SCHEM")).orElse(rs.getString("PKTABLE_CAT"))
            );
        };

        tableChilds(ForeignKey.class, table.mutator()::addNewForeignKey, supplier, mutator, progressListener);
    }

    protected  void tableChilds(
        final Class type,
        final Supplier childSupplier,
        final SqlSupplier resultSetSupplier,
        final AbstractDbmsOperationHandler.TableChildMutator resultSetMutator,
        final ProgressMeasure progressListener
    ) {
        tableChilds(type, childSupplier, resultSetSupplier, resultSetMutator, rs -> true, progressListener);
    }

    protected  void tableChilds(
        final Class type,
        final Supplier childSupplier,
        final SqlSupplier resultSetSupplier,
        final AbstractDbmsOperationHandler.TableChildMutator resultSetMutator,
        final SqlPredicate filter,
        final ProgressMeasure progressListener
    ) {
        requireNonNulls(childSupplier, resultSetSupplier, resultSetMutator);

        final long begin = System.currentTimeMillis();
        try (final ResultSet rsChild = resultSetSupplier.get()) {

            if (SHOW_METADATA) {
                final ResultSetMetaData rsmd = rsChild.getMetaData();
                final int numberOfColumns = rsmd.getColumnCount();
                for (int x = 1; x <= numberOfColumns; x++) {
                    final int columnType = rsmd.getColumnType(x);
                    LOGGER.info(x + ":" + rsmd.getColumnName(x) + ", " + rsmd.getColumnClassName(x) + ", " + columnType);
                }
            }

            while (rsChild.next()) {
                if (SHOW_METADATA) {
                    final ResultSetMetaData rsmd = rsChild.getMetaData();
                    final int numberOfColumns = rsmd.getColumnCount();
                    for (int x = 1; x <= numberOfColumns; x++) {
                        final Object val = rsChild.getObject(x);
                        LOGGER.info(x + ":" + rsmd.getColumnName(x) + ":'" + val + "'");
                    }
                }
                if (filter.test(rsChild)) {
                    resultSetMutator.mutate(childSupplier.get(), rsChild);
                } else {
                    LOGGER.info("Skipped due to RS filtering. This is normal for some DBMS types.");
                }
            }
        } catch (final SQLException sqle) {
            LOGGER.error(sqle, "Unable to read table child.");
            throw new SpeedmentException(sqle);
        }
        final long duration = System.currentTimeMillis() - begin;
        timers.computeIfAbsent(type, $ -> new AtomicLong()).addAndGet(duration);
    }

    /**
     * Sets the autoIncrement property of a Column.
     *
     * @param column to use
     * @param md that contains column metadata (per connection.getMetaData().getColumns(...))
     * @throws SQLException  if something goes wrong in JDBC
     */
    protected void setAutoIncrement(Column column, ColumnMetaData md) throws SQLException {
        final String isAutoIncrementString = md.getIsAutoincrement();
        final String isGeneratedColumnString = md.getIsGeneratedcolumn();

        if ("YES".equalsIgnoreCase(isAutoIncrementString) 
        ||  "YES".equalsIgnoreCase(isGeneratedColumnString)) {
            column.mutator().setAutoIncrement(true);
        }
    }

    /**
     * Returns the schema lookup name used when calling
     * connection.getMetaData().getXxxx(y, schemaLookupName, ...) methods.
     *
     * @param schema to use
     * @return the schema lookup name used when calling
     * connection.getMetaData().getXxxx(y, schemaLookupName, ...) methods
     */
    protected String jdbcSchemaLookupName(Schema schema) {
        return null;
    }

    /**
     * Returns the catalog lookup name used when calling
     * connection.getMetaData().getXxxx(catalogLookupName, ...) methods.
     *
     * @param schema to use
     * @return the catalog lookup name used when calling
     * connection.getMetaData().getXxxx(catalogLookupName, ...) methods
     */
    protected String jdbcCatalogLookupName(Schema schema) {
        return schema.getId();
    }

    /**
     * Returns the table name used when calling the
     * connection.getMetaData().getColumns() method.
     *
     * @param table to use
     * @return the table name used when calling
     * connection.getMetaData().getColumns() method
     */
    protected String metaDataTableNameForColumns(Table table) {
        return table.getId();
    }

    /**
     * Returns the table name used when calling the
     * connection.getMetaData().getIndexes() method.
     *
     * @param table to use
     * @return the table name used when calling
     * connection.getMetaData().getIndexes() method
     */
    protected String metaDataTableNameForIndexes(Table table) {
        return table.getId();
    }

    /**
     * Returns the table name used when calling the
     * connection.getMetaData().getPrimaryKeys() method.
     *
     * @param table to use
     * @return the table name used when calling the
     * connection.getMetaData().getPrimaryKeys() method
     */
    protected String metaDataTableNameForPrimaryKeys(Table table) {
        return table.getId();
    }

    /**
     * Returns the table name used when calling the
     * connection.getMetaData().getImportedKeys() method.
     *
     * @param table to use
     * @return the table name used when calling the
     * connection.getMetaData().getImportedKeys() method
     */
    protected String metaDataTableNameForForeignKeys(Table table) {
        return table.getId();
    }
    
    /**
     * Returns the table name used when calling the
     * {@code SHOW COLUMNS FROM} statement.
     * 
     * @param table  to use
     * @return       the table name to use
     */
    protected String metaDataTableNameForShowColumns(Table table) {
        return table.getId();
    }
    
    protected Map> readTypeMapFromDB(Dbms dbms) throws SQLException {
        requireNonNull(dbms);

        final List typeInfoMetaDataList = new ArrayList<>();
        try (final Connection connection = getConnection(dbms)) {
            try (final ResultSet rs = connection.getMetaData().getTypeInfo()) {
                while (rs.next()) {
                    final TypeInfoMetaData typeInfo = TypeInfoMetaData.of(rs);
                    typeInfoMetaDataList.add(typeInfo);
                }
            }
            return typeMapFromTypeInfo(typeInfoMetaDataList);
        }
    }

    protected Map> readTypeMapFromSet(Set typeInfos) {
        requireNonNull(typeInfos);

        return typeMapFromTypeInfo(new ArrayList<>(typeInfos));
    }

    protected Map> typeMapFromTypeInfo(List typeInfoMetaDataList) {
        requireNonNull(typeInfoMetaDataList);

        final Map> result = newCaseInsensitiveMap();
        // First, put the java.sql.Types mapping for all types
        typeInfoMetaDataList.forEach(ti -> {
            final Optional javaSqlTypeName = ti.javaSqlTypeName();

            javaSqlTypeName.ifPresent(tn -> {
                final Class mappedClass = javaTypeMap.get(tn);
                if (mappedClass != null) {
                    result.put(tn, mappedClass);
                }
            });
        });

        // Then, put the typeInfo sqlName (That may be more specific) for all types
        typeInfoMetaDataList.forEach(ti -> {
            final String key = ti.getSqlTypeName();
            final Class mappedClass = javaTypeMap.get(key);
            if (mappedClass != null) {
                result.put(key, mappedClass);
            } else {
                final Optional javaSqlTypeName = ti.javaSqlTypeName();
                javaSqlTypeName.ifPresent(ltn -> {
                    final Class lookupMappedClass = javaTypeMap.get(ltn);
                    if (lookupMappedClass != null) {
                        result.put(key, lookupMappedClass);
                    }
                });
            }
        });
        return result;
    }

    private 

> String actionName(D doc) { return doc.mainInterface().getSimpleName() + " " + doc.getId() + " in " + doc.getParentOrThrow().getId(); } /** * Queries the database for a list of ENUM constants belonging to the specified table and * column. * * @param dbms the dbms * @param table the table * @param columnName the column name * @return list of enum constants. * @throws SQLException if an error occured */ protected List enumConstantsOf(Dbms dbms, Table table, String columnName) throws SQLException { final DbmsType dbmsType = dbmsTypeOf(dbmsHandlerComponent, dbms); final DatabaseNamingConvention naming = dbmsType.getDatabaseNamingConvention(); final String sql = String.format( "show columns from %s where field=%s;", naming.fullNameOf(table), naming.quoteField(columnName) ); try (final Connection conn = getConnection(dbms); final PreparedStatement ps = conn.prepareStatement(sql); final ResultSet rs = ps.executeQuery()) { if (rs.next()) { final String fullResult = rs.getString(2); if (fullResult.startsWith("enum('") && fullResult.endsWith("')")) { final String middle = fullResult.substring(5, fullResult.length() - 1); return Stream.of(middle.split(",")) .map(s -> s.substring(1, s.length() - 1)) .filter(s -> !s.isEmpty()) .collect(toList()); } else { throw new SpeedmentException("Unexpected response (" + fullResult + ")."); } } else { throw new SpeedmentException("Expected an result."); } } } private Connection getConnection(Dbms dbms) { return connectionPoolComponent.getConnection(dbms); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy