diff --git a/pom.xml b/pom.xml index 9138223..7fd83ce 100644 --- a/pom.xml +++ b/pom.xml @@ -15,70 +15,71 @@ ~ from Levels Beyond Incorporated. --> - - 4.0.0 - AutoFKIndexDB - AutoFKIndexDB - 0.0.1-SNAPSHOT - - src - - - resources - - **/*.java - - - - - - maven-compiler-plugin - 3.1 - - 1.7 - 1.7 - - - - maven-assembly-plugin - - - - true - com.levelsbeyond.postgres.AutoFKIndexDB - - - - jar-with-dependencies - - - - - make-my-jar-with-dependencies - package - - single - - - - - - - - - org.apache.commons - commons-lang3 - 3.3 - - - org.jdom - jdom - 1.1.3 - - - postgresql - postgresql - 9.1-901-1.jdbc4 - - + + 4.0.0 + AutoFKIndexDB + AutoFKIndexDB + 0.0.2-SNAPSHOT + + src + + + resources + + **/*.java + + + + + + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + + + maven-assembly-plugin + + + + true + com.levelsbeyond.postgres.AutoFKIndexDB + + + + jar-with-dependencies + + + + + make-my-jar-with-dependencies + package + + single + + + + + + + + + org.apache.commons + commons-lang3 + 3.3 + + + org.jdom + jdom + 1.1.3 + + + postgresql + postgresql + 9.1-901-1.jdbc4 + + \ No newline at end of file diff --git a/src/com/levelsbeyond/postgres/AutoFKIndexDB.java b/src/com/levelsbeyond/postgres/AutoFKIndexDB.java index 5db38e7..643d378 100644 --- a/src/com/levelsbeyond/postgres/AutoFKIndexDB.java +++ b/src/com/levelsbeyond/postgres/AutoFKIndexDB.java @@ -33,310 +33,304 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; - import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; +import org.postgresql.util.PSQLException; public class AutoFKIndexDB { - @SuppressWarnings("unused") - private static final XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat()); - - private static String constraintQuery = - "SELECT tc.table_name, kcu.column_name, tc.constraint_name " + - "FROM information_schema.table_constraints tc " + - "LEFT JOIN information_schema.key_column_usage kcu " + - "ON tc.constraint_catalog = kcu.constraint_catalog " + - "AND tc.constraint_schema = kcu.constraint_schema " + - "AND tc.constraint_name = kcu.constraint_name " + - "WHERE lower(tc.constraint_type) = 'foreign key'"; - - private static final String tuplesQuery = "SELECT reltuples::bigint FROM pg_constraint JOIN pg_class ON (conrelid = pg_class.oid) WHERE contype = 'f' AND conname = ?"; - - private static final String hasIndexQuery = "SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = ? AND n.nspname = 'public'"; - - private static final String createIndex = "CREATE INDEX %s ON public.%s (%s)"; - - private static final String indexQuery = "SELECT indexname FROM pg_indexes WHERE schemaname = 'public' AND indexdef like 'CREATE INDEX%'"; - - private static final Map> indexMap = new HashMap>(); - - public static void main(String[] args) throws SQLException, IOException { - String host = "localhost"; - String database = null; - String user = null; - String password = null; - int minTuples = 1000; - Boolean dropIndices = null; - Boolean dropConstraints = null; - Boolean createIndices = null; - boolean showHelp = false; - - for (String arg : args) { - if (arg.startsWith("--host=")) { - host = arg.substring(7); - } - else if (arg.startsWith("--database=")) { - database = arg.substring(11); - } - else if (arg.startsWith("--user=")) { - user = arg.substring(7); - } - else if (arg.startsWith("--password=")) { - password = arg.substring(11); - } - else if (arg.startsWith("--min-tuples=")) { - minTuples = Integer.valueOf(arg.substring(13)); - } - else if (arg.startsWith("--drop-indices=")) { - dropIndices = BooleanUtils.toBooleanObject(arg.substring(15)); - } - else if (arg.startsWith("--drop-constraints=")) { - dropConstraints = BooleanUtils.toBooleanObject(arg.substring(19)); - } - else if (arg.startsWith("--create-indices=")) { - createIndices = BooleanUtils.toBooleanObject(arg.substring(17)); - } - else if (arg.startsWith("--help")) { - showHelp = true; - } - } - - Properties props = new Properties(); - props.load(ClassLoader.getSystemResourceAsStream("default.index.properties")); - try { - props.load(new FileInputStream(getJarPath() + File.separator + "index.properties")); - } - catch (Exception e) { - System.err.println("Couldn't locate index.properties -- ignoring."); - } - for (Entry prop : props.entrySet()) { - String indexName = prop.getKey().toString(); - String[] tableAndColumn = prop.getValue().toString().split("\\."); - if (tableAndColumn.length == 2) { - indexMap.put(indexName, new ImmutablePair(tableAndColumn[0], tableAndColumn[1])); - } - } - - if (showHelp || database == null || user == null || password == null || (dropIndices == null && dropConstraints == null && createIndices == null)) { - System.out.println("Usage: java -jar AutoFKIndexDB.jar\nOptions:\n" + - "\t--host=[" + host + "]\n" + - "\t--database=[" + database + "]\n" + - "\t--user=[" + user + "]\n" + - "\t--password=[" + password + "]\n" + - "\t--min-tuples=[" + minTuples + "]\n" + - "\t--drop-indices=" + (dropIndices != null ? "[" + dropIndices + "]" : "true|false") + "\n" + - "\t--drop-constraints=" + (dropConstraints != null ? "[" + dropConstraints + "]" : "true|false") + "\n" + - "\t--create-indices=" + (createIndices != null ? "[" + createIndices + "]" : "true|false") + "\n" + - "\t--help"); - System.exit(1); - } - - Connection conn = null; - try { - String url = "jdbc:postgresql://" + host + ":5432/" + database; - conn = DriverManager.getConnection(url, user, password); - - AutoFKIndexDB autoFK = new AutoFKIndexDB(); - - if (BooleanUtils.isTrue(dropIndices)) { - autoFK.dropForeignKeyIndices(conn); - } - - if (BooleanUtils.isTrue(dropConstraints)) { - autoFK.dropForeignKeyConstraints(conn); - } - - if (BooleanUtils.isTrue(createIndices)) { - autoFK.createForeignKeyIndices(conn, minTuples); - } - } - catch (Exception e) { - System.err.println("Got an exception: " + e.getMessage()); - e.printStackTrace(); - } - finally { - if (conn != null) { - conn.close(); - } - } - } - - private static String jarPath; - - private static String getJarPath() { - if (jarPath == null) { - try { - File jarFile = new File(AutoFKIndexDB.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - jarPath = jarFile.getParentFile().getAbsolutePath(); - } - catch (URISyntaxException e) { - e.printStackTrace(); - } - } - return jarPath; - } - - private void createForeignKeyIndices(Connection conn, int minTuples) throws SQLException { - List fkConstraints = new ArrayList(); - PreparedStatement selectStmt = conn.prepareStatement(constraintQuery); - ResultSet rs = selectStmt.executeQuery(); - while (rs.next()) { - String tableName = rs.getString(1); - String columnName = rs.getString(2); - String constraintName = rs.getString(3); - fkConstraints.add(new ForeignKeyConstraint(tableName, columnName, constraintName)); - } - - for (ForeignKeyConstraint fk : fkConstraints) { - selectStmt = conn.prepareStatement(tuplesQuery); - selectStmt.setString(1, fk.getConstraintName()); - rs = selectStmt.executeQuery(); - if (rs.next()) { - fk.setTuples(rs.getLong(1)); - } - } - - Collections.sort(fkConstraints); - - for (ForeignKeyConstraint fk : fkConstraints) { - String indexName = "fki_" + fk.getConstraintName(); - if (indexName.lastIndexOf("_fkey") > 0) { - indexName = indexName.substring(0, indexName.lastIndexOf("_fkey")); - } - if (fk.getTuples() >= minTuples && !hasIndex(conn, indexName)) { - System.out.printf("Creating index %s (tuples: %d)\n", (fk.getTableName() + "." + fk.getColumnName() + " --> " + indexName), fk.getTuples()); - - PreparedStatement createStmt = conn.prepareStatement(String.format(createIndex, indexName, fk.getTableName(), fk.getColumnName())); - createStmt.execute(); - } - } - - for (Entry> entry : indexMap.entrySet()) { - String indexName = entry.getKey(); - String tableName = entry.getValue().getLeft(); - String columnName = entry.getValue().getRight(); - - if (!hasIndex(conn, indexName)) { - System.out.printf("Creating index %s\n", (tableName + "." + columnName + " --> " + indexName)); - - try { - PreparedStatement createStmt = conn.prepareStatement(String.format(createIndex, indexName, tableName, columnName)); - createStmt.execute(); - } - catch (SQLException e) { - System.err.println("Failed to create index " + indexName + ": " + e.getMessage()); - } - } - } - } - - private void dropForeignKeyConstraints(Connection conn) throws SQLException { - List fkConstraints = new ArrayList(); - PreparedStatement selectStmt = conn.prepareStatement(constraintQuery); - ResultSet rs = selectStmt.executeQuery(); - while (rs.next()) { - String tableName = rs.getString(1); - String columnName = rs.getString(2); - String constraintName = rs.getString(3); - fkConstraints.add(new ForeignKeyConstraint(tableName, columnName, constraintName)); - } - - Collections.sort(fkConstraints); - - for (ForeignKeyConstraint fk : fkConstraints) { - PreparedStatement alterStmt = conn.prepareStatement("ALTER TABLE " + fk.tableName + " DROP CONSTRAINT " + fk.constraintName); - System.out.println("Dropping constraint " + fk.tableName + "." + fk.constraintName); - alterStmt.execute(); - } - } - - private boolean hasIndex(Connection conn, String name) throws SQLException { - PreparedStatement selectStmt = conn.prepareStatement(hasIndexQuery); - selectStmt.setString(1, name); - ResultSet rs = selectStmt.executeQuery(); - return rs.next(); - } - - private void dropForeignKeyIndices(Connection conn) throws SQLException { - List indices = new ArrayList(); - - PreparedStatement selectStmt = conn.prepareStatement(indexQuery); - ResultSet rs = selectStmt.executeQuery(); - while (rs.next()) { - indices.add(rs.getString(1)); - } - - for (String indexName : indices) { - PreparedStatement dropStmt = conn.prepareStatement("DROP INDEX " + indexName); - System.out.println("Dropping index " + indexName); - dropStmt.execute(); - } - } - - public class ForeignKeyConstraint implements Comparable { - private String tableName; - private String columnName; - private String constraintName; - private Long tuples; - - public ForeignKeyConstraint(String tableName, String columnName, String constraintName) { - super(); - this.tableName = tableName; - this.columnName = columnName; - this.constraintName = constraintName; - } - - public String getTableName() { - return tableName; - } - - public void setTableName(String tableName) { - this.tableName = tableName; - } - - public String getColumnName() { - return columnName; - } - - public void setColumnName(String columnName) { - this.columnName = columnName; - } - - public String getConstraintName() { - return constraintName; - } - - public void setConstraintName(String constraintName) { - this.constraintName = constraintName; - } - - public Long getTuples() { - return tuples; - } - - public void setTuples(Long tuples) { - this.tuples = tuples; - } - - @Override - public int compareTo(ForeignKeyConstraint that) { - int result = 0; - if (this.tuples != null && that.tuples != null) { - result = that.tuples.compareTo(this.tuples); - } - if (result == 0) { - result = this.tableName.compareTo(that.tableName); - } - if (result == 0) { - result = this.columnName.compareTo(that.columnName); - } - if (result == 0) { - result = this.constraintName.compareTo(that.constraintName); - } - return result; - } - } + + private static final XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat()); + + private static String constraintQuery = + "SELECT tc.table_name, kcu.column_name, tc.constraint_name " + + "FROM information_schema.table_constraints tc " + + "LEFT JOIN information_schema.key_column_usage kcu " + + "ON tc.constraint_catalog = kcu.constraint_catalog " + + "AND tc.constraint_schema = kcu.constraint_schema " + + "AND tc.constraint_name = kcu.constraint_name " + + "WHERE lower(tc.constraint_type) = 'foreign key'"; + + private static final String tuplesQuery = "SELECT reltuples::bigint FROM pg_constraint JOIN pg_class ON (conrelid = pg_class.oid) WHERE contype = 'f' AND conname = ?"; + + private static final String hasIndexQuery = "SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = ? AND n.nspname = 'public'"; + + private static final String createIndex = "CREATE INDEX %s ON public.%s (%s)"; + + private static final String indexQuery = "SELECT indexname FROM pg_indexes WHERE schemaname = 'public' AND indexdef like 'CREATE INDEX%'"; + + private static final Map> indexMap = new HashMap<>(); + + public static void main(String[] args) throws SQLException, IOException { + String host = "localhost"; + String database = null; + String user = null; + String password = null; + int minTuples = 0; + Boolean dropIndices = null; + Boolean dropConstraints = null; + Boolean createIndices = null; + boolean showHelp = false; + + for (String arg : args) { + if (arg.startsWith("--host=")) { + host = arg.substring(7); + } else if (arg.startsWith("--database=")) { + database = arg.substring(11); + } else if (arg.startsWith("--user=")) { + user = arg.substring(7); + } else if (arg.startsWith("--password=")) { + password = arg.substring(11); + } else if (arg.startsWith("--min-tuples=")) { + minTuples = Integer.valueOf(arg.substring(13)); + } else if (arg.startsWith("--drop-indices=")) { + dropIndices = BooleanUtils.toBooleanObject(arg.substring(15)); + } else if (arg.startsWith("--drop-constraints=")) { + dropConstraints = BooleanUtils.toBooleanObject(arg.substring(19)); + } else if (arg.startsWith("--create-indices=")) { + createIndices = BooleanUtils.toBooleanObject(arg.substring(17)); + } else if (arg.startsWith("--help")) { + showHelp = true; + } + } + + Properties props = new Properties(); + props.load(ClassLoader.getSystemResourceAsStream("default.index.properties")); + try { + props.load(new FileInputStream(getJarPath() + File.separator + "index.properties")); + } catch (Exception e) { + System.err.println("Couldn't locate index.properties -- ignoring."); + } + for (Entry prop : props.entrySet()) { + String indexName = prop.getKey().toString(); + String[] tableAndColumn = prop.getValue().toString().split("\\."); + if (tableAndColumn.length == 2) { + indexMap.put(indexName, new ImmutablePair<>(tableAndColumn[0], tableAndColumn[1])); + } + } + + if (showHelp || database == null || user == null || password == null || (dropIndices == null && dropConstraints == null && createIndices == null)) { + System.out.println("Usage: java -jar AutoFKIndexDB.jar\nOptions:\n" + + "\t--host=[" + host + "]\n" + + "\t--database=[" + database + "]\n" + + "\t--user=[" + user + "]\n" + + "\t--password=[" + password + "]\n" + + "\t--min-tuples=[" + minTuples + "]\n" + + "\t--drop-indices=" + (dropIndices != null ? "[" + dropIndices + "]" : "true|false") + "\n" + + "\t--drop-constraints=" + (dropConstraints != null ? "[" + dropConstraints + "]" : "true|false") + "\n" + + "\t--create-indices=" + (createIndices != null ? "[" + createIndices + "]" : "true|false") + "\n" + + "\t--help"); + System.exit(1); + } + + Connection conn = null; + try { + String url = "jdbc:postgresql://" + host + ":5432/" + database; + conn = DriverManager.getConnection(url, user, password); + + AutoFKIndexDB autoFK = new AutoFKIndexDB(); + + if (BooleanUtils.isTrue(dropIndices)) { + autoFK.dropForeignKeyIndices(conn); + } + + if (BooleanUtils.isTrue(dropConstraints)) { + autoFK.dropForeignKeyConstraints(conn); + } + + if (BooleanUtils.isTrue(createIndices)) { + autoFK.createForeignKeyIndices(conn, minTuples); + } + } catch (Exception e) { + System.err.println("Got an exception: " + e.getMessage()); + e.printStackTrace(); + } finally { + if (conn != null) { + conn.close(); + } + } + } + + private static String jarPath; + + private static String getJarPath() { + if (jarPath == null) { + try { + File jarFile = new File(AutoFKIndexDB.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + jarPath = jarFile.getParentFile().getAbsolutePath(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + return jarPath; + } + + private void createForeignKeyIndices(Connection conn, int minTuples) throws SQLException { + List fkConstraints = new ArrayList<>(); + PreparedStatement selectStmt = conn.prepareStatement(constraintQuery); + ResultSet rs = selectStmt.executeQuery(); + while (rs.next()) { + String tableName = rs.getString(1); + String columnName = rs.getString(2); + String constraintName = rs.getString(3); + if (!constraintName.contains("-")) { + fkConstraints.add(new ForeignKeyConstraint(tableName, columnName, constraintName)); + } + } + + for (ForeignKeyConstraint fk : fkConstraints) { + selectStmt = conn.prepareStatement(tuplesQuery); + selectStmt.setString(1, fk.getConstraintName()); + rs = selectStmt.executeQuery(); + if (rs.next()) { + fk.setTuples(rs.getLong(1)); + } + } + + Collections.sort(fkConstraints); + + for (ForeignKeyConstraint fk : fkConstraints) { + String indexName = "fki_" + fk.getConstraintName(); + if (indexName.lastIndexOf("_fkey") > 0) { + indexName = indexName.substring(0, indexName.lastIndexOf("_fkey")); + } + if (fk.getTuples() >= minTuples && !hasIndex(conn, indexName)) { + System.out.printf("Creating index %s (tuples: %d)\n", (fk.getTableName() + "." + fk.getColumnName() + " --> " + indexName), fk.getTuples()); + try { + PreparedStatement createStmt = conn.prepareStatement(String.format(createIndex, indexName, fk.getTableName(), fk.getColumnName())); + createStmt.execute(); + } catch (PSQLException e) { + System.out.println(e.getMessage()); + System.out.println(String.format(createIndex, indexName, fk.getTableName(), fk.getColumnName())); + } + } + } + + for (Entry> entry : indexMap.entrySet()) { + String indexName = entry.getKey(); + String tableName = entry.getValue().getLeft(); + String columnName = entry.getValue().getRight(); + + if (!hasIndex(conn, indexName)) { + System.out.printf("Creating index %s\n", (tableName + "." + columnName + " --> " + indexName)); + + try { + PreparedStatement createStmt = conn.prepareStatement(String.format(createIndex, indexName, tableName, columnName)); + createStmt.execute(); + } catch (SQLException e) { + System.err.println("Failed to create index " + indexName + ": " + e.getMessage()); + } + } + } + } + + private void dropForeignKeyConstraints(Connection conn) throws SQLException { + List fkConstraints = new ArrayList<>(); + PreparedStatement selectStmt = conn.prepareStatement(constraintQuery); + ResultSet rs = selectStmt.executeQuery(); + while (rs.next()) { + String tableName = rs.getString(1); + String columnName = rs.getString(2); + String constraintName = rs.getString(3); + fkConstraints.add(new ForeignKeyConstraint(tableName, columnName, constraintName)); + } + + Collections.sort(fkConstraints); + + for (ForeignKeyConstraint fk : fkConstraints) { + PreparedStatement alterStmt = conn.prepareStatement("ALTER TABLE " + fk.tableName + " DROP CONSTRAINT " + fk.constraintName); + System.out.println("Dropping constraint " + fk.tableName + "." + fk.constraintName); + alterStmt.execute(); + } + } + + private boolean hasIndex(Connection conn, String name) throws SQLException { + PreparedStatement selectStmt = conn.prepareStatement(hasIndexQuery); + selectStmt.setString(1, name); + ResultSet rs = selectStmt.executeQuery(); + return rs.next(); + } + + private void dropForeignKeyIndices(Connection conn) throws SQLException { + List indices = new ArrayList<>(); + + PreparedStatement selectStmt = conn.prepareStatement(indexQuery); + ResultSet rs = selectStmt.executeQuery(); + while (rs.next()) { + indices.add(rs.getString(1)); + } + + for (String indexName : indices) { + PreparedStatement dropStmt = conn.prepareStatement("DROP INDEX " + indexName); + System.out.println("Dropping index " + indexName); + dropStmt.execute(); + } + } + + public class ForeignKeyConstraint implements Comparable { + + private String tableName; + private String columnName; + private String constraintName; + private Long tuples; + + public ForeignKeyConstraint(String tableName, String columnName, String constraintName) { + super(); + this.tableName = tableName; + this.columnName = columnName; + this.constraintName = constraintName; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + public String getConstraintName() { + return constraintName; + } + + public void setConstraintName(String constraintName) { + this.constraintName = constraintName; + } + + public Long getTuples() { + return tuples; + } + + public void setTuples(Long tuples) { + this.tuples = tuples; + } + + @Override + public int compareTo(ForeignKeyConstraint that) { + int result = 0; + if (this.tuples != null && that.tuples != null) { + result = that.tuples.compareTo(this.tuples); + } + if (result == 0) { + result = this.tableName.compareTo(that.tableName); + } + if (result == 0) { + result = this.columnName.compareTo(that.columnName); + } + if (result == 0) { + result = this.constraintName.compareTo(that.constraintName); + } + return result; + } + } }