@Test public void triggers() throws Exception { // Set up information schema properties final InformationSchemaViews informationSchemaViews = new InformationSchemaViews(); informationSchemaViews.setTriggersSql("SELECT * FROM INFORMATION_SCHEMA.TRIGGERS"); final SchemaCrawlerOptions schemaCrawlerOptions = new SchemaCrawlerOptions(); schemaCrawlerOptions.setInformationSchemaViews(informationSchemaViews); schemaCrawlerOptions.setSchemaInfoLevel(SchemaInfoLevel.maximum()); final Database database = getDatabase(schemaCrawlerOptions); final Schema schema = new SchemaReference("PUBLIC", "BOOKS"); final Table[] tables = database.getTables(schema).toArray(new Table[0]); boolean foundTrigger = false; for (final Table table : tables) { for (final Trigger trigger : table.getTriggers()) { foundTrigger = true; assertEquals( "Triggers full name does not match", "PUBLIC.BOOKS.AUTHORS.TRG_AUTHORS", trigger.getFullName()); assertEquals( "Trigger EventManipulationType does not match", EventManipulationType.delete, trigger.getEventManipulationType()); } } assertTrue("No triggers found", foundTrigger); }
/** * Retrieves additional table attributes from the database. * * @throws SQLException On a SQL exception */ void retrieveAdditionalTableAttributes() throws SQLException { final InformationSchemaViews informationSchemaViews = getRetrieverConnection().getInformationSchemaViews(); if (!informationSchemaViews.hasAdditionalTableAttributesSql()) { LOGGER.log(Level.FINE, "Additional table attributes SQL statement was not provided"); return; } final String tableAttributesSql = informationSchemaViews.getAdditionalTableAttributesSql(); final Connection connection = getDatabaseConnection(); try (final Statement statement = connection.createStatement(); final MetadataResultSet results = new MetadataResultSet(statement.executeQuery(tableAttributesSql)); ) { while (results.next()) { final String catalogName = quotedName(results.getString("TABLE_CATALOG")); final String schemaName = quotedName(results.getString("TABLE_SCHEMA")); final String tableName = quotedName(results.getString("TABLE_NAME")); LOGGER.log(Level.FINER, "Retrieving additional table attributes: " + tableName); final MutableTable table = lookupTable(catalogName, schemaName, tableName); if (table == null) { LOGGER.log( Level.FINE, String.format("Cannot find table, %s.%s.%s", catalogName, schemaName, tableName)); continue; } table.addAttributes(results.getAttributes()); } } catch (final Exception e) { LOGGER.log(Level.WARNING, "Could not retrieve additional table attributes", e); } }
@Test public void synonyms() throws Exception { final String[] classes = { "MutableTable", "MutableTable", "MutableTable", "", "", "", }; final String[] synonymNames = { "AUTHORS", "BOOKAUTHORS", "BOOKS", "\"Global Counts\"", "No_Columns", "PUBLICATIONS", }; final InformationSchemaViews informationSchemaViews = new InformationSchemaViews(); informationSchemaViews.setSynonymSql( "SELECT LIMIT 1 3 \n" + " TABLE_CATALOG AS SYNONYM_CATALOG, \n" + " TABLE_SCHEMA AS SYNONYM_SCHEMA, \n" + " TABLE_NAME AS SYNONYM_NAME, \n" + " TABLE_CATALOG AS REFERENCED_OBJECT_CATALOG, \n" + " TABLE_SCHEMA AS REFERENCED_OBJECT_SCHEMA, \n" + " TABLE_NAME AS REFERENCED_OBJECT_NAME \n" + "FROM \n" + " INFORMATION_SCHEMA.TABLES \n" + "WHERE \n" + " TABLE_SCHEMA = 'BOOKS' \n" + "UNION \n" + "SELECT LIMIT 1 3 \n" + " 'PUBLIC' AS SYNONYM_CATALOG, \n" + " 'BOOKS' AS SYNONYM_SCHEMA, \n" + " TABLE_NAME AS SYNONYM_NAME, \n" + " TABLE_CATALOG AS REFERENCED_OBJECT_CATALOG, \n" + " TABLE_SCHEMA AS REFERENCED_OBJECT_SCHEMA, \n" + " TABLE_NAME + '1' AS REFERENCED_OBJECT_NAME \n" + "FROM \n" + " INFORMATION_SCHEMA.TABLES \n" + "WHERE \n" + " TABLE_SCHEMA != 'BOOKS' "); final SchemaInfoLevel minimum = SchemaInfoLevel.minimum(); minimum.setRetrieveSynonymInformation(true); final SchemaCrawlerOptions schemaCrawlerOptions = new SchemaCrawlerOptions(); schemaCrawlerOptions.setSchemaInfoLevel(minimum); schemaCrawlerOptions.setInformationSchemaViews(informationSchemaViews); schemaCrawlerOptions.setSynonymInclusionRule(InclusionRule.INCLUDE_ALL); final Database database = getDatabase(schemaCrawlerOptions); final Schema schema = database.getSchema("PUBLIC.BOOKS"); assertNotNull("BOOKS Schema not found", schema); final Synonym[] synonyms = database.getSynonyms(schema).toArray(new Synonym[0]); assertEquals("Synonym count does not match", 6, synonyms.length); for (int i = 0; i < synonyms.length; i++) { final Synonym synonym = synonyms[i]; assertNotNull(synonym); assertEquals( "Wrong referenced object class - " + synonym.getReferencedObject().getClass(), classes[i], synonym.getReferencedObject().getClass().getSimpleName()); assertEquals("", synonymNames[i], synonym.getName()); } }
void retrieveForeignKeys(final NamedObjectList<MutableTable> allTables) throws SchemaCrawlerSQLException { requireNonNull(allTables); final InformationSchemaViews informationSchemaViews = getRetrieverConnection().getInformationSchemaViews(); if (!informationSchemaViews.hasForeignKeysSql()) { LOGGER.log(Level.INFO, "Retrieving foreign keys using database metadata"); retrieveForeignKeysUsingDatabaseMetadata(allTables); } else { LOGGER.log(Level.INFO, "Retrieving foreign keys using SQL"); retrieveForeignKeysUsingSql(informationSchemaViews); } }
@Test public void viewDefinitions() throws Exception { final InformationSchemaViews informationSchemaViews = new InformationSchemaViews(); informationSchemaViews.setViewsSql("SELECT * FROM INFORMATION_SCHEMA.VIEWS"); final SchemaCrawlerOptions schemaCrawlerOptions = new SchemaCrawlerOptions(); schemaCrawlerOptions.setTableTypes("VIEW"); schemaCrawlerOptions.setInformationSchemaViews(informationSchemaViews); schemaCrawlerOptions.setSchemaInfoLevel(SchemaInfoLevel.maximum()); final Database database = getDatabase(schemaCrawlerOptions); final Schema schema = new SchemaReference("PUBLIC", "BOOKS"); final View view = (View) database.getTable(schema, "AUTHORSLIST"); assertNotNull("View not found", view); assertNotNull("View definition not found", view.getDefinition()); assertFalse("View definition not found", view.getDefinition().trim().equals("")); }
/** * Retrieves a view information from the database, in the INFORMATION_SCHEMA format. * * @throws SQLException On a SQL exception */ void retrieveViewInformation() throws SQLException { final InformationSchemaViews informationSchemaViews = getRetrieverConnection().getInformationSchemaViews(); if (!informationSchemaViews.hasViewsSql()) { LOGGER.log(Level.FINE, "Views SQL statement was not provided"); return; } final String viewInformationSql = informationSchemaViews.getViewsSql(); final Connection connection = getDatabaseConnection(); try (final Statement statement = connection.createStatement(); final MetadataResultSet results = new MetadataResultSet(statement.executeQuery(viewInformationSql)); ) { while (results.next()) { final String catalogName = quotedName(results.getString("TABLE_CATALOG")); final String schemaName = quotedName(results.getString("TABLE_SCHEMA")); final String viewName = quotedName(results.getString("TABLE_NAME")); final MutableView view = (MutableView) lookupTable(catalogName, schemaName, viewName); if (view == null) { LOGGER.log( Level.FINE, String.format("Cannot find table, %s.%s.%s", catalogName, schemaName, viewName)); continue; } LOGGER.log(Level.FINER, "Retrieving view information: " + viewName); final String definition = results.getString("VIEW_DEFINITION"); final CheckOptionType checkOption = results.getEnum("CHECK_OPTION", CheckOptionType.unknown); final boolean updatable = results.getBoolean("IS_UPDATABLE"); view.appendDefinition(definition); view.setCheckOption(checkOption); view.setUpdatable(updatable); view.addAttributes(results.getAttributes()); } } catch (final Exception e) { LOGGER.log(Level.WARNING, "Could not retrieve views", e); } }
@Test public void routineDefinitions() throws Exception { final InformationSchemaViews informationSchemaViews = new InformationSchemaViews(); informationSchemaViews.setRoutinesSql("SELECT * FROM INFORMATION_SCHEMA.ROUTINES"); final SchemaCrawlerOptions schemaCrawlerOptions = new SchemaCrawlerOptions(); schemaCrawlerOptions.setInformationSchemaViews(informationSchemaViews); schemaCrawlerOptions.setSchemaInfoLevel(SchemaInfoLevel.maximum()); final Database database = getDatabase(schemaCrawlerOptions); final Schema schema = new SchemaReference("PUBLIC", "BOOKS"); final Routine[] routines = database.getRoutines(schema).toArray(new Routine[0]); assertEquals("Wrong number of routines", 4, routines.length); for (final Routine routine : routines) { assertFalse( "Routine definition not found, for " + routine, Utility.isBlank(routine.getDefinition())); } }
private void retrieveForeignKeysUsingSql(final InformationSchemaViews informationSchemaViews) throws SchemaCrawlerSQLException { final String fkSql = informationSchemaViews.getForeignKeysSql(); LOGGER.log(Level.FINER, String.format("Executing SQL to retrieve foreign keys: %n%s", fkSql)); final NamedObjectList<MutableForeignKey> foreignKeys = new NamedObjectList<>(); final Connection connection = getDatabaseConnection(); try (final Statement statement = connection.createStatement(); final MetadataResultSet results = new MetadataResultSet(executeSql(statement, fkSql)); ) { createForeignKeys(results, foreignKeys); } catch (final SQLException e) { throw new SchemaCrawlerSQLException("Could not retrieve foreign keys from SQL:\n" + fkSql, e); } }
/** * Retrieves a trigger information from the database, in the INFORMATION_SCHEMA format. * * @throws SQLException On a SQL exception */ void retrieveTriggerInformation() throws SQLException { final InformationSchemaViews informationSchemaViews = getRetrieverConnection().getInformationSchemaViews(); if (!informationSchemaViews.hasTriggerSql()) { LOGGER.log(Level.FINE, "Trigger definition SQL statement was not provided"); return; } final String triggerInformationSql = informationSchemaViews.getTriggersSql(); final Connection connection = getDatabaseConnection(); try (final Statement statement = connection.createStatement(); final MetadataResultSet results = new MetadataResultSet(statement.executeQuery(triggerInformationSql)); ) { while (results.next()) { final String catalogName = quotedName(results.getString("TRIGGER_CATALOG")); final String schemaName = quotedName(results.getString("TRIGGER_SCHEMA")); final String triggerName = quotedName(results.getString("TRIGGER_NAME")); LOGGER.log(Level.FINER, "Retrieving trigger: " + triggerName); // "EVENT_OBJECT_CATALOG", "EVENT_OBJECT_SCHEMA" final String tableName = results.getString("EVENT_OBJECT_TABLE"); final MutableTable table = lookupTable(catalogName, schemaName, tableName); if (table == null) { LOGGER.log( Level.FINE, String.format("Cannot find table, %s.%s.%s", catalogName, schemaName, tableName)); continue; } final EventManipulationType eventManipulationType = results.getEnum("EVENT_MANIPULATION", EventManipulationType.unknown); final int actionOrder = results.getInt("ACTION_ORDER", 0); final String actionCondition = results.getString("ACTION_CONDITION"); final String actionStatement = results.getString("ACTION_STATEMENT"); final ActionOrientationType actionOrientation = results.getEnum("ACTION_ORIENTATION", ActionOrientationType.unknown); String conditionTimingString = results.getString("ACTION_TIMING"); if (conditionTimingString == null) { conditionTimingString = results.getString("CONDITION_TIMING"); } final ConditionTimingType conditionTiming = ConditionTimingType.valueOfFromValue(conditionTimingString); MutableTrigger trigger = table.lookupTrigger(triggerName); if (trigger == null) { trigger = new MutableTrigger(table, triggerName); } trigger.setEventManipulationType(eventManipulationType); trigger.setActionOrder(actionOrder); trigger.appendActionCondition(actionCondition); trigger.appendActionStatement(actionStatement); trigger.setActionOrientation(actionOrientation); trigger.setConditionTiming(conditionTiming); trigger.addAttributes(results.getAttributes()); // Add trigger to the table table.addTrigger(trigger); } } catch (final Exception e) { LOGGER.log(Level.WARNING, "Could not retrieve triggers", e); } }
/** * Retrieves a check constraint information from the database, in the INFORMATION_SCHEMA format. * * @throws SQLException On a SQL exception */ void retrieveCheckConstraintInformation() throws SQLException { final Map<String, MutableCheckConstraint> checkConstraintsMap = new HashMap<>(); final InformationSchemaViews informationSchemaViews = getRetrieverConnection().getInformationSchemaViews(); if (!informationSchemaViews.hasTableConstraintsSql()) { LOGGER.log(Level.FINE, "Table constraints SQL statement was not provided"); return; } final String tableConstraintsInformationSql = informationSchemaViews.getTableConstraintsSql(); final Connection connection = getDatabaseConnection(); try (final Statement statement = connection.createStatement(); final MetadataResultSet results = new MetadataResultSet(statement.executeQuery(tableConstraintsInformationSql)); ) { while (results.next()) { final String catalogName = quotedName(results.getString("CONSTRAINT_CATALOG")); final String schemaName = quotedName(results.getString("CONSTRAINT_SCHEMA")); final String constraintName = quotedName(results.getString("CONSTRAINT_NAME")); LOGGER.log(Level.FINER, "Retrieving constraint: " + constraintName); // "TABLE_CATALOG", "TABLE_SCHEMA" final String tableName = quotedName(results.getString("TABLE_NAME")); final MutableTable table = lookupTable(catalogName, schemaName, tableName); if (table == null) { LOGGER.log( Level.FINE, String.format("Cannot find table, %s.%s.%s", catalogName, schemaName, tableName)); continue; } final String constraintType = results.getString("CONSTRAINT_TYPE"); final boolean deferrable = results.getBoolean("IS_DEFERRABLE"); final boolean initiallyDeferred = results.getBoolean("INITIALLY_DEFERRED"); if (constraintType.equalsIgnoreCase("check")) { final MutableCheckConstraint checkConstraint = new MutableCheckConstraint(table, constraintName); checkConstraint.setDeferrable(deferrable); checkConstraint.setInitiallyDeferred(initiallyDeferred); checkConstraint.addAttributes(results.getAttributes()); // Add to map, since we will need this later final String constraintFullName = table.getSchema().getFullName() + "." + constraintName; checkConstraintsMap.put(constraintFullName, checkConstraint); } } } catch (final Exception e) { LOGGER.log(Level.WARNING, "Could not retrieve check constraint information", e); return; } if (!informationSchemaViews.hasCheckConstraintsSql()) { LOGGER.log(Level.FINE, "Check constraints SQL statement was not provided"); return; } final String checkConstraintInformationSql = informationSchemaViews.getCheckConstraintsSql(); // Get check constraint definitions try (final Statement statement = connection.createStatement(); final MetadataResultSet results = new MetadataResultSet(statement.executeQuery(checkConstraintInformationSql)); ) { while (results.next()) { final String catalogName = quotedName(results.getString("CONSTRAINT_CATALOG")); final String schemaName = quotedName(results.getString("CONSTRAINT_SCHEMA")); final String constraintName = quotedName(results.getString("CONSTRAINT_NAME")); LOGGER.log(Level.FINER, "Retrieving constraint definition: " + constraintName); String definition = results.getString("CHECK_CLAUSE"); final String constraintFullName = new SchemaReference(catalogName, schemaName) + "." + constraintName; final MutableCheckConstraint checkConstraint = checkConstraintsMap.get(constraintFullName); if (checkConstraint == null) { LOGGER.log(Level.FINEST, "Could not add check constraint to table: " + constraintName); continue; } final String text = checkConstraint.getDefinition(); if (!Utility.isBlank(text)) { definition = checkConstraint.getDefinition() + definition; } checkConstraint.setDefinition(definition); } } catch (final Exception e) { LOGGER.log(Level.WARNING, "Could not retrieve check constraints", e); } // Add check constraints to tables final Collection<MutableCheckConstraint> checkConstraintsCollection = checkConstraintsMap.values(); for (final MutableCheckConstraint checkConstraint : checkConstraintsCollection) { final MutableTable table = (MutableTable) checkConstraint.getParent(); table.addCheckConstraint(checkConstraint); } }
@Test public void checkConstraints() throws Exception { final int[] tableCounts = {6, 5, 0, 0, 2, 0}; final int[][] checkConstraintCounts = { {4, 0, 2, 3, 0, 1, 0}, { 3, 0, 0, 3, 6, }, {}, {}, {4, 2}, {} }; final String[][][] checkConstraintNames = { { {"CHECK_UPPERCASE_STATE", "SYS_CT_10068", "SYS_CT_10069", "SYS_CT_10070"}, {}, {"SYS_CT_10083", "SYS_CT_10084"}, {"SYS_CT_10076", "SYS_CT_10077", "SYS_CT_10078"}, {}, {"SYS_CT_10065"}, {} }, { {"SYS_CT_10144", "SYS_CT_10145", "SYS_CT_10146"}, {}, {}, { "SYS_CT_10133", "SYS_CT_10134", "SYS_CT_10135", }, { "CHECK_UPPERCASE_STATE", "SYS_CT_10121", "SYS_CT_10122", "SYS_CT_10123", "SYS_CT_10124", "SYS_CT_10125", }, }, {}, {}, { {"SYS_CT_10105", "SYS_CT_10106", "SYS_CT_10107", "SYS_CT_10108"}, {"SYS_CT_10114", "SYS_CT_10115"} }, {} }; final InformationSchemaViews informationSchemaViews = new InformationSchemaViews(); informationSchemaViews.setTableConstraintsSql( "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS"); informationSchemaViews.setCheckConstraintsSql( "SELECT * FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS"); final SchemaCrawlerOptions schemaCrawlerOptions = new SchemaCrawlerOptions(); schemaCrawlerOptions.setSchemaInfoLevel(SchemaInfoLevel.maximum()); schemaCrawlerOptions.setInformationSchemaViews(informationSchemaViews); final Database database = getDatabase(schemaCrawlerOptions); final Schema[] schemas = database.getSchemas().toArray(new Schema[0]); assertEquals("Schema count does not match", 6, schemas.length); for (int schemaIdx = 0; schemaIdx < schemas.length; schemaIdx++) { final Schema schema = schemas[schemaIdx]; final Table[] tables = database.getTables(schema).toArray(new Table[0]); assertEquals("Table count does not match", tableCounts[schemaIdx], tables.length); for (int tableIdx = 0; tableIdx < tables.length; tableIdx++) { final Table table = tables[tableIdx]; final CheckConstraint[] checkConstraints = table.getCheckConstraints().toArray(new CheckConstraint[0]); assertEquals( String.format( "Table [%d][%d] %s check constraints count does not match", schemaIdx, tableIdx, table.getFullName()), checkConstraintCounts[schemaIdx][tableIdx], checkConstraints.length); for (int i = 0; i < checkConstraints.length; i++) { final CheckConstraint checkConstraint = checkConstraints[i]; assertEquals( "Check constraint name does not match for table " + table, checkConstraintNames[schemaIdx][tableIdx][i], checkConstraint.getName()); } } } }
@Test public void counts() throws Exception { final int[] tableCounts = { 6, 0, 0, 2, 0, }; final int[][] tableColumnCounts = { {9, 3, 3, 6, 1, 2}, {}, {}, {4, 5}, {}, }; final int[][] checkConstraints = { {4, 0, 2, 3, 0, 1, 0}, {}, {}, {4, 2}, {}, }; final int[][] indexCounts = { { 3, 0, 3, 1, 0, 1, }, {}, {}, {1, 1}, {}, }; final int[][] fkCounts = { { 1, 0, 2, 1, 0, 0, }, {}, {}, {1, 1}, {}, }; final int[][] exportedFkCounts = { { 1, 0, 0, 1, 0, 0, }, {}, {}, {1, 0}, {}, }; final int[][] importedFkCounts = { { 0, 0, 2, 0, 0, 0, }, {}, {}, {0, 1}, {}, }; final int[][] tablePrivilegesCounts = { {6, 6, 6, 6, 6, 6, 6}, {}, {}, { 6, 6, }, {}, }; final InformationSchemaViews informationSchemaViews = new InformationSchemaViews(); informationSchemaViews.setTableConstraintsSql( "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS"); informationSchemaViews.setCheckConstraintsSql( "SELECT * FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS"); final SchemaCrawlerOptions schemaCrawlerOptions = new SchemaCrawlerOptions(); schemaCrawlerOptions.setSchemaInfoLevel(SchemaInfoLevel.maximum()); schemaCrawlerOptions.setInformationSchemaViews(informationSchemaViews); schemaCrawlerOptions.setSchemaInclusionRule( new InclusionRule(InclusionRule.ALL, ".*\\.FOR_LINT")); final Database database = getDatabase(schemaCrawlerOptions); final Schema[] schemas = database.getSchemas().toArray(new Schema[0]); assertEquals("Schema count does not match", 5, schemas.length); for (int schemaIdx = 0; schemaIdx < schemas.length; schemaIdx++) { final Schema schema = schemas[schemaIdx]; final Table[] tables = database.getTables(schema).toArray(new Table[0]); assertEquals("Table count does not match", tableCounts[schemaIdx], tables.length); Arrays.sort(tables, NamedObjectSort.alphabetical); for (int tableIdx = 0; tableIdx < tables.length; tableIdx++) { final Table table = tables[tableIdx]; assertEquals( String.format( "Table [%d][%d] %s columns count does not match", schemaIdx, tableIdx, table.getFullName()), tableColumnCounts[schemaIdx][tableIdx], table.getColumns().size()); assertEquals( String.format( "Table [%d][%d] %s check constraints count does not match", schemaIdx, tableIdx, table.getFullName()), checkConstraints[schemaIdx][tableIdx], table.getCheckConstraints().size()); assertEquals( String.format( "Table [%d][%d] %s index count does not match", schemaIdx, tableIdx, table.getFullName()), indexCounts[schemaIdx][tableIdx], table.getIndices().size()); assertEquals( String.format( "Table [%d][%d] %s foreign key count does not match", schemaIdx, tableIdx, table.getFullName()), fkCounts[schemaIdx][tableIdx], table.getForeignKeys().size()); assertEquals( String.format( "Table [%d][%d] %s exported foreign key count does not match", schemaIdx, tableIdx, table.getFullName()), exportedFkCounts[schemaIdx][tableIdx], table.getExportedForeignKeys().size()); assertEquals( String.format( "Table [%d][%d] %s imported foreign key count does not match", schemaIdx, tableIdx, table.getFullName()), importedFkCounts[schemaIdx][tableIdx], table.getImportedForeignKeys().size()); assertEquals( String.format( "Table [%d][%d] %s privileges count does not match", schemaIdx, tableIdx, table.getFullName()), tablePrivilegesCounts[schemaIdx][tableIdx], table.getPrivileges().size()); } } }