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

cdc.util.rdb.tools.RdbMeta Maven / Gradle / Ivy

package cdc.util.rdb.tools;

import java.io.PrintStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import cdc.util.cli.AbstractMainSupport;

/**
 * Utility to print meta information about a database.
 *
 * @author Damien Carbonne
 *
 */
public final class RdbMeta {
    protected static final Logger LOGGER = LogManager.getLogger(RdbMeta.class);
    private Connection connection = null;
    private final DatabaseMetaData metadata;
    private final PrintStream out = System.out;
    private List tables = null;

    private RdbMeta(MainArgs margs)
            throws SQLException {
        connection = DriverManager.getConnection(margs.url, margs.user, margs.password);
        metadata = connection.getMetaData();
        init();
    }

    public static class MainArgs {
        /** Database url. */
        public String url;
        /** User name. */
        public String user;
        /** User password. */
        public String password;
    }

    protected static class Table {
        String catalog;
        String schema;
        String name;
    }

    private void init() throws SQLException {
        if (tables == null) {
            tables = new ArrayList<>();
            try (final ResultSet rs = metadata.getTables(null, null, null, null)) {
                while (rs.next()) {
                    final Table table = new Table();
                    table.catalog = rs.getString(1);
                    table.schema = rs.getString(2);
                    table.name = rs.getString(3);
                }
            }
        }
    }

    public void close() throws SQLException {
        if (connection != null) {
            connection.close();
            connection = null;
        }
    }

    public void print(ResultSetMetaData meta) throws SQLException {
        out.println("  =====================================================================");
        for (int index = 1; index <= meta.getColumnCount(); index++) {
            out.printf("  %2d %-25s %-15s %s%n",
                       index,
                       meta.getColumnName(index),
                       meta.getColumnTypeName(index),
                       meta.getColumnClassName(index));
        }
        out.println("  =====================================================================");
    }

    public int print(String title,
                     ResultSet rs) throws SQLException {
        return print(title, rs, 0, true);
    }

    public int print(String title,
                     ResultSet rs,
                     int count,
                     boolean first) throws SQLException {
        int icount = count;
        final ResultSetMetaData meta = rs.getMetaData();
        if (first) {
            out.println();
            out.println(title);
            print(meta);
        }

        while (rs.next()) {
            out.print(" ");
            icount++;
            out.printf("%5d", count);
            for (int index = 1; index <= meta.getColumnCount(); index++) {
                final String className = meta.getColumnClassName(index);
                final boolean needsQuotes;
                if ("java.lang.String".equals(className)) {
                    needsQuotes = true;
                } else {
                    needsQuotes = false;
                }
                if (needsQuotes && rs.getObject(index) != null) {
                    out.print(" '" + rs.getObject(index) + "'");
                } else {
                    out.print(" " + rs.getObject(index));
                }
            }
            out.println();
        }
        return icount;

    }

    private void printSupports(String name,
                               boolean value) {
        out.printf("Supports %-55s %b%n", name + ":", value);
    }

    private void print() throws SQLException {
        printGeneral();

        printCatalogs();
        printSchemas();

        printTypeInfo();
        printIndexInfo();
        printAttributes();
        printClientInfoProperties();
        printFunctions();
        printFunctionColumns();

        printProcedures();
        printProcedureColumns();

        printTableTypes();
        printTables();
        printColumns();
        printPseudoColumns();
        printColumnPrivileges();

        printPrimaryKeys();
        printExportedKeys();
        printImportedKeys();
        printCrossReference();
    }

    @FunctionalInterface
    private static interface BooleanSupplier {
        public boolean get() throws Exception;
    }

    private void show(BooleanSupplier s) {
        try {
            out.println(s.getClass().getCanonicalName());
            final boolean result = s.get();
            out.println(result);
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printGeneral() throws SQLException {
        show(metadata::allProceduresAreCallable);

        out.println("GENERAL");

        out.println("All Procedures Are Callable: " + metadata.allProceduresAreCallable());
        out.println("All Tables Are Selectable: " + metadata.allTablesAreSelectable());
        out.println("Auto Commit Failure Closes All ResultSets: " + metadata.autoCommitFailureClosesAllResultSets());
        out.println("Data Definition Causes Transaction Commit: " + metadata.dataDefinitionCausesTransactionCommit());
        out.println("Data Definition Ignored In Transactions: " + metadata.dataDefinitionIgnoredInTransactions());
        out.println("Does Max Row Size Include Blobs: " + metadata.doesMaxRowSizeIncludeBlobs());
        out.println("Generated Key Always Returned: " + metadata.generatedKeyAlwaysReturned());
        out.println("Is Catalog At Start: " + metadata.isCatalogAtStart());
        out.println("Is Read Only: " + metadata.isReadOnly());
        out.println("Locators Update Copy: " + metadata.locatorsUpdateCopy());
        out.println("Null Plus Non Null Is Null: " + metadata.nullPlusNonNullIsNull());
        out.println("Nulls Are Sorted At End: " + metadata.nullsAreSortedAtEnd());
        out.println("Nulls Are Sorted At Start: " + metadata.nullsAreSortedAtStart());
        out.println("Nulls Are Sorted High: " + metadata.nullsAreSortedHigh());
        out.println("Nulls Are Sorted Low: " + metadata.nullsAreSortedLow());
        out.println("Stores Lower Case Identifiers: " + metadata.storesLowerCaseIdentifiers());
        out.println("Stores Lower Case Quoted Identifiers: " + metadata.storesLowerCaseQuotedIdentifiers());
        out.println("Stores Mixed Case Identifiers: " + metadata.storesMixedCaseIdentifiers());
        out.println("Stores Mixed Case Quoted Identifiers: " + metadata.storesMixedCaseQuotedIdentifiers());
        out.println("Stores Upper Case Identifiers: " + metadata.storesUpperCaseIdentifiers());
        out.println("Stores Upper Case Quoted Identifiers: " + metadata.storesUpperCaseQuotedIdentifiers());
        out.println("Uses Local File Per Table: " + metadata.usesLocalFilePerTable());
        out.println("Uses Local Files: " + metadata.usesLocalFiles());

        out.println("URL: " + metadata.getURL());
        out.println("User Name: " + metadata.getUserName());

        out.println("JDBC Major Version: " + metadata.getJDBCMajorVersion());
        out.println("JDBC Minor Version: " + metadata.getJDBCMinorVersion());

        out.println("Driver Major Version: " + metadata.getDriverMajorVersion());
        out.println("Driver Minor Version: " + metadata.getDriverMinorVersion());
        out.println("Driver Name: " + metadata.getDriverName());
        out.println("Driver Version: " + metadata.getDriverVersion());

        out.println("Database Product Name: " + metadata.getDatabaseProductName());
        out.println("Database Product Version: " + metadata.getDatabaseProductVersion());
        out.println("Database Major Version: " + metadata.getDatabaseMajorVersion());
        out.println("Database Minor Version: " + metadata.getDatabaseMinorVersion());

        out.println("Catalog Separator: '" + metadata.getCatalogSeparator() + "'");

        out.println("Catalog Term: " + metadata.getCatalogTerm());
        out.println("Schema Term: " + metadata.getSchemaTerm());
        out.println("Procedure Term: " + metadata.getProcedureTerm());

        out.println("Default Transaction Isolation: " + metadata.getDefaultTransactionIsolation());

        out.println("Extra Name Characters: '" + metadata.getExtraNameCharacters() + "'");
        out.println("Identifier Quote String: '" + metadata.getIdentifierQuoteString() + "'");

        out.println("Max Binary Literal Length: " + metadata.getMaxBinaryLiteralLength());
        out.println("Max Catalog Name Length  : " + metadata.getMaxCatalogNameLength());
        out.println("Max Char Literal Length  : " + metadata.getMaxCharLiteralLength());
        out.println("Max Column Name Length   : " + metadata.getMaxColumnNameLength());
        out.println("Max Columns In Group By  : " + metadata.getMaxColumnsInGroupBy());
        out.println("Max Columns In Index     : " + metadata.getMaxColumnsInIndex());
        out.println("Max Columns In Order By  : " + metadata.getMaxColumnsInOrderBy());
        out.println("Max Columns In Select    : " + metadata.getMaxColumnsInSelect());
        out.println("Max Columns In Table     : " + metadata.getMaxColumnsInTable());
        out.println("Max Connections          : " + metadata.getMaxConnections());
        out.println("Max Cursor Name Length   : " + metadata.getMaxCursorNameLength());
        out.println("Max Index Length         : " + metadata.getMaxIndexLength());
        out.println("Max Logical Lob Size     : " + metadata.getMaxLogicalLobSize());
        out.println("Max Procedure Name Length: " + metadata.getMaxProcedureNameLength());
        out.println("Max Row Size             : " + metadata.getMaxRowSize());
        out.println("Max Schema Name Length   : " + metadata.getMaxSchemaNameLength());
        out.println("Max Statement Length     : " + metadata.getMaxStatementLength());
        out.println("Max Statments            : " + metadata.getMaxStatements());
        out.println("Max Table Name Length    : " + metadata.getMaxTableNameLength());
        out.println("Max Tables In Select     : " + metadata.getMaxTablesInSelect());
        out.println("Max User Name Length     : " + metadata.getMaxUserNameLength());

        out.println("Numeric Functions: " + metadata.getNumericFunctions());
        out.println("String Functions: " + metadata.getStringFunctions());
        out.println("System Functions: " + metadata.getSystemFunctions());
        out.println("Time Date Functions: " + metadata.getTimeDateFunctions());

        out.println("ResultSet Holdability: " + metadata.getResultSetHoldability());
        out.println("RowId Lifetime: " + metadata.getRowIdLifetime());
        out.println("Search String Escape: '" + metadata.getSearchStringEscape() + "'");

        out.println("SQL Keywords: " + metadata.getSQLKeywords());
        out.println("SQL State Type: " + metadata.getSQLStateType());

        printSupports("Alter Table With Add Column", metadata.supportsAlterTableWithAddColumn());
        printSupports("Alter Table With Drop Column", metadata.supportsAlterTableWithDropColumn());

        printSupports("ANSI92 Entry Level SQL", metadata.supportsANSI92EntryLevelSQL());
        printSupports("ANSI92 Full SQL", metadata.supportsANSI92FullSQL());
        printSupports("ANSI92 Intermediate SQL", metadata.supportsANSI92IntermediateSQL());

        printSupports("Batch Updates", metadata.supportsBatchUpdates());

        printSupports("Catalogs In Data Manipulation", metadata.supportsCatalogsInDataManipulation());
        printSupports("Catalogs In Index Definitions", metadata.supportsCatalogsInIndexDefinitions());
        printSupports("Catalogs In Privilege Definitions", metadata.supportsCatalogsInPrivilegeDefinitions());
        printSupports("Catalogs In Procedure Calls", metadata.supportsCatalogsInProcedureCalls());
        printSupports("Catalogs In Table Definitions", metadata.supportsCatalogsInTableDefinitions());

        printSupports("Column Aliasing", metadata.supportsColumnAliasing());

        printSupports("Convert", metadata.supportsConvert());

        printSupports("Core SQL Grammar", metadata.supportsCoreSQLGrammar());
        printSupports("Minimum SQL Grammar", metadata.supportsMinimumSQLGrammar());
        printSupports("Extended SQL Grammar", metadata.supportsExtendedSQLGrammar());

        printSupports("Correlated Subqueries", metadata.supportsCorrelatedSubqueries());
        printSupports("Data Definition And Data Manipulation Transactions",
                      metadata.supportsDataDefinitionAndDataManipulationTransactions());
        printSupports("Data Manipulation Transactions Only", metadata.supportsDataManipulationTransactionsOnly());
        printSupports("Different Table Correlation Names", metadata.supportsDifferentTableCorrelationNames());
        printSupports("Expressions In Order By", metadata.supportsExpressionsInOrderBy());
        printSupports("Full Outer Joins", metadata.supportsFullOuterJoins());
        printSupports("Get Generated Keys", metadata.supportsGetGeneratedKeys());

        printSupports("Group By", metadata.supportsGroupBy());
        printSupports("Group By Beyond Select", metadata.supportsGroupByBeyondSelect());
        printSupports("Group By Unrelated", metadata.supportsGroupByUnrelated());

        printSupports("Integrity Enhancement Facility", metadata.supportsIntegrityEnhancementFacility());
        printSupports("Like Escape Clause", metadata.supportsLikeEscapeClause());

        printSupports("Mixed Case Identifiers", metadata.supportsMixedCaseIdentifiers());
        printSupports("Mixed Case Quoted Identifiers", metadata.supportsMixedCaseQuotedIdentifiers());

        printSupports("Multiple Open Results", metadata.supportsMultipleOpenResults());
        printSupports("Multiple ResultSets", metadata.supportsMultipleResultSets());
        printSupports("Multiple Transactions", metadata.supportsMultipleTransactions());
        printSupports("Named Parameters", metadata.supportsNamedParameters());
        printSupports("Non Nullable Columns", metadata.supportsNonNullableColumns());
        printSupports("Open Cursors Across Commit", metadata.supportsOpenCursorsAcrossCommit());
        printSupports("Open Cursors Across Rollback", metadata.supportsOpenCursorsAcrossRollback());
        printSupports("Open Statements Across Commit", metadata.supportsOpenStatementsAcrossCommit());
        printSupports("Open Statements Across Rollback", metadata.supportsOpenStatementsAcrossRollback());
        printSupports("Order By Unrelated", metadata.supportsOrderByUnrelated());

        printSupports("Limited Outer Joins", metadata.supportsLimitedOuterJoins());
        printSupports("Outer Joins", metadata.supportsOuterJoins());

        printSupports("Positioned Delete", metadata.supportsPositionedDelete());
        printSupports("Positioned Update", metadata.supportsPositionedUpdate());

        printSupports("Ref Cursors", metadata.supportsRefCursors());
        printSupports("Savepoints", metadata.supportsSavepoints());

        printSupports("Schemas In Data Manipulation", metadata.supportsSchemasInDataManipulation());
        printSupports("Schemas In Index Definitions", metadata.supportsSchemasInIndexDefinitions());
        printSupports("Schemas In Privilege Definitions", metadata.supportsSchemasInPrivilegeDefinitions());
        printSupports("Schemas In Procedure Calls", metadata.supportsSchemasInProcedureCalls());
        printSupports("Schemas In Table Definitions", metadata.supportsSchemasInTableDefinitions());

        printSupports("Select For Update", metadata.supportsSelectForUpdate());
        printSupports("Statement Pooling", metadata.supportsStatementPooling());
        printSupports("Stored Functions Using Call Syntax", metadata.supportsStoredFunctionsUsingCallSyntax());
        printSupports("Stored Procedures", metadata.supportsStoredProcedures());

        printSupports("Subqueries In Comparisons", metadata.supportsSubqueriesInComparisons());
        printSupports("Subqueries In Exists", metadata.supportsSubqueriesInExists());
        printSupports("Subqueries In Ins", metadata.supportsSubqueriesInIns());
        printSupports("Subqueries In Quantifieds", metadata.supportsSubqueriesInQuantifieds());

        printSupports("Table Correlation Names", metadata.supportsTableCorrelationNames());
        printSupports("Transactions", metadata.supportsTransactions());
        printSupports("Union", metadata.supportsUnion());
        printSupports("Union All", metadata.supportsUnionAll());
    }

    private void printCatalogs() {
        try {
            try (final ResultSet rs = metadata.getCatalogs()) {
                print("CATALOGS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printSchemas() {
        try {
            try (final ResultSet rs = metadata.getSchemas()) {
                print("SCHEMAS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printClientInfoProperties() {
        try {
            try (final ResultSet rs = metadata.getClientInfoProperties()) {
                print("CLIENT INFO PROPERTIES:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printIndexInfo() {
        try {
            int count = 0;
            boolean first = true;
            for (final Table table : tables) {
                try (final ResultSet rs = metadata.getIndexInfo(table.catalog, table.schema, table.name, false, false)) {
                    count = print("INDEX INFO:", rs, count, first);
                    first = false;
                }
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printAttributes() {
        try {
            try (final ResultSet rs = metadata.getAttributes(null, null, null, null)) {
                print("ATTRIBUTES:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printColumnPrivileges() {
        try {
            int count = 0;
            boolean first = true;
            for (final Table table : tables) {
                try (final ResultSet rs = metadata.getColumnPrivileges(table.catalog, table.schema, table.name, null)) {
                    count = print("COLUMN PRIVILEGES:", rs, count, first);
                    first = false;
                }
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printCrossReference() {
        // TODO optimize
        try {
            int count = 0;
            boolean first = true;
            for (final Table ptable : tables) {
                for (final Table ftable : tables) {
                    try (final ResultSet rs = metadata.getCrossReference(ptable.catalog,
                                                                         ptable.schema,
                                                                         ptable.name,
                                                                         ftable.catalog,
                                                                         ftable.schema,
                                                                         ftable.name)) {
                        count = print("CROSS REFERENCE:", rs, count, first);
                        first = false;
                    }
                }
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printTypeInfo() {
        try {
            try (final ResultSet rs = metadata.getTypeInfo()) {
                print("TYPE INFO:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printFunctions() {
        try {
            try (final ResultSet rs = metadata.getFunctions(null, null, null)) {
                print("FUNCTIONS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printFunctionColumns() {
        try {
            try (final ResultSet rs = metadata.getFunctionColumns(null, null, null, null)) {
                print("FUNCTION COLUMNS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printProcedures() {
        try {
            try (final ResultSet rs = metadata.getProcedures(null, null, null)) {
                print("PROCEDURES:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printProcedureColumns() {
        try {
            try (final ResultSet rs = metadata.getProcedureColumns(null, null, null, null)) {
                print("PROCEDURE COLUMNS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printTables() {
        try {
            try (final ResultSet rs = metadata.getTables(null, null, null, null)) {
                print("TABLES:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printTableTypes() {
        try {
            try (final ResultSet rs = metadata.getTableTypes()) {
                print("TABLE TYPES:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printColumns() {
        try {
            try (final ResultSet rs = metadata.getColumns(null, null, null, null)) {
                print("COLUMNS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printPseudoColumns() {
        try {
            try (final ResultSet rs = metadata.getPseudoColumns(null, null, null, null)) {
                print("PSEUDO COLUMNS:", rs);
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printPrimaryKeys() {
        try {
            int count = 0;
            boolean first = true;
            for (final Table table : tables) {
                try (final ResultSet rs = metadata.getPrimaryKeys(table.catalog, table.schema, table.name)) {
                    count = print("PRIMARY KEYS:", rs, count, first);
                    first = false;
                }
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printExportedKeys() {
        try {
            int count = 0;
            boolean first = true;
            for (final Table table : tables) {
                try (final ResultSet rs = metadata.getExportedKeys(table.catalog, table.schema, table.name)) {
                    count = print("EXPORTED KEYS:", rs, count, first);
                    first = false;
                }
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    private void printImportedKeys() {
        try {
            int count = 0;
            boolean first = true;
            for (final Table table : tables) {
                try (final ResultSet rs = metadata.getImportedKeys(table.catalog, table.schema, table.name)) {
                    count = print("IMPORTED KEYS:", rs, count, first);
                    first = false;
                }
            }
        } catch (final Exception e) {
            LOGGER.catching(e);
        }
    }

    public static void execute(MainArgs margs) throws Exception {
        final RdbMeta meta = new RdbMeta(margs);
        meta.print();
        meta.close();
    }

    public static void main(String[] args) {
        final MainSupport support = new MainSupport();
        support.main(args);
    }

    private static class MainSupport extends AbstractMainSupport {
        public MainSupport() {
            super(RdbMeta.class, LOGGER);
        }

        @Override
        protected String getVersion() {
            return cdc.util.Config.VERSION;
        }

        @Override
        protected void addSpecificOptions(Options options) {
            options.addOption(Option.builder()
                                    .longOpt(URL)
                                    .desc("URL to access database.")
                                    .hasArg()
                                    .required()
                                    .build());

            options.addOption(Option.builder()
                                    .longOpt(USER)
                                    .desc("Optional user name.")
                                    .hasArg()
                                    .build());

            options.addOption(Option.builder("pwd")
                                    .longOpt(PASSWORD)
                                    .desc("Optional user password.")
                                    .hasArg()
                                    .build());
        }

        @Override
        protected MainArgs analyze(CommandLine cl) throws ParseException {
            final MainArgs margs = new MainArgs();
            margs.url = cl.getOptionValue(URL);
            margs.user = cl.getOptionValue(USER);
            margs.password = cl.getOptionValue(PASSWORD);
            return margs;
        }

        @Override
        protected Void execute(MainArgs margs) throws Exception {
            RdbMeta.execute(margs);
            return null;
        }
    }
}