diff --git a/src/main/java/cz/startnet/utils/pgdiff/PgDiffTables.java b/src/main/java/cz/startnet/utils/pgdiff/PgDiffTables.java index 3fc5f8ac..7ce20544 100644 --- a/src/main/java/cz/startnet/utils/pgdiff/PgDiffTables.java +++ b/src/main/java/cz/startnet/utils/pgdiff/PgDiffTables.java @@ -9,6 +9,7 @@ import cz.startnet.utils.pgdiff.schema.PgColumnUtils; import cz.startnet.utils.pgdiff.schema.PgSchema; import cz.startnet.utils.pgdiff.schema.PgTable; +import cz.startnet.utils.pgdiff.util.ColumnComparator; import java.io.PrintWriter; import java.text.MessageFormat; import java.util.ArrayList; @@ -246,8 +247,9 @@ private static void addAlterStorage(final PrintWriter writer, private static void addCreateTableColumns(final List statements, final PgDiffArguments arguments, final PgTable oldTable, final PgTable newTable, final List dropDefaultsColumns) { + ColumnComparator comparator = new ColumnComparator(oldTable, newTable); for (final PgColumn column : newTable.getColumns()) { - if (!oldTable.containsColumn(column.getName())) { + if (comparator.hasNotColumn(column.getName())) { statements.add("\tADD COLUMN " + column.getFullDefinition(arguments.isAddDefaults())); @@ -269,8 +271,9 @@ private static void addCreateTableColumns(final List statements, */ private static void addDropTableColumns(final List statements, final PgTable oldTable, final PgTable newTable) { + ColumnComparator comparator = new ColumnComparator(newTable, oldTable); for (final PgColumn column : oldTable.getColumns()) { - if (!newTable.containsColumn(column.getName())) { + if (comparator.hasNotColumn(column.getName())) { statements.add("\tDROP COLUMN " + PgDiffUtils.getQuotedName(column.getName())); } @@ -290,15 +293,26 @@ private static void addDropTableColumns(final List statements, private static void addModifyTableColumns(final List statements, final PgDiffArguments arguments, final PgTable oldTable, final PgTable newTable, final List dropDefaultsColumns) { + ColumnComparator comparator = new ColumnComparator(oldTable, newTable); for (final PgColumn newColumn : newTable.getColumns()) { - if (!oldTable.containsColumn(newColumn.getName())) { + if (comparator.hasNotColumn(newColumn.getName())) { continue; } - final PgColumn oldColumn = oldTable.getColumn(newColumn.getName()); + final PgColumn oldColumn = comparator.getColumn(newColumn.getName()); + final String oldColumnName = + PgDiffUtils.getQuotedName(oldColumn.getName()); final String newColumnName = PgDiffUtils.getQuotedName(newColumn.getName()); + if (!oldColumn.getName().equals(newColumn.getName())) { + statements.add("\tRENAME COLUMN " + oldColumnName + " TO " + newColumnName + " /* " + + MessageFormat.format( + Resources.getString("RenameColumn"), + newTable.getName(), oldColumn.getName(), + newColumn.getName()) + " */"); + } + if (!oldColumn.getType().equals(newColumn.getType())) { statements.add("\tALTER COLUMN " + newColumnName + " TYPE " + newColumn.getType() + " /* " @@ -307,6 +321,7 @@ private static void addModifyTableColumns(final List statements, newTable.getName(), oldColumn.getType(), newColumn.getType()) + " */"); } + final String oldDefault = (oldColumn.getDefaultValue() == null) ? "" : oldColumn.getDefaultValue(); diff --git a/src/main/java/cz/startnet/utils/pgdiff/util/ColumnComparator.java b/src/main/java/cz/startnet/utils/pgdiff/util/ColumnComparator.java new file mode 100644 index 00000000..1824d9d6 --- /dev/null +++ b/src/main/java/cz/startnet/utils/pgdiff/util/ColumnComparator.java @@ -0,0 +1,131 @@ +/** + * Copyright 2006 StartNet s.r.o. + * + * Distributed under MIT license + */ +package cz.startnet.utils.pgdiff.util; + +import cz.startnet.utils.pgdiff.schema.PgColumn; +import cz.startnet.utils.pgdiff.schema.PgTable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Compare column tables + * + * @author byorty + */ +public class ColumnComparator { + + private HashMap columns; + + public ColumnComparator(PgTable oldTable, PgTable newTable) { + this.columns = new HashMap(); + HashMap diffOldColumns = new HashMap(); + HashMap diffNewColumns = new HashMap(); + List newColumns = newTable.getColumns(); + for (int i = 0;i < newColumns.size();++i) { + PgColumn newColumn = newTable.getColumns().get(i); + PgColumn oldColumn = oldTable.getColumn(newColumn.getName()); + if (null == oldColumn && false == diffNewColumns.containsKey(i)) { + diffNewColumns.put(i, newColumn); + } else { + this.columns.put(newColumn.getName(), oldColumn); + } + } + List oldColumns = oldTable.getColumns(); + for (int i = 0;i < oldColumns.size();++i) { + PgColumn oldColumn = oldColumns.get(i); + if (false == this.columns.containsKey(oldColumn.getName())) { + diffOldColumns.put(i, oldColumn); + } + } + for (Map.Entry oldEntry : diffOldColumns.entrySet()) { + Integer oldColumnNumber = oldEntry.getKey(); + PgColumn oldColumn = oldEntry.getValue(); + String oldColumnName = oldColumn.getName(); + char[] oldColumnChars = oldColumnName.toCharArray(); + int containsCount = 0; + int diffCount = -1; + HashMap> variants = new HashMap>(); + for (Map.Entry newEntry : diffNewColumns.entrySet()) { + Integer newColumnNumber = newEntry.getKey(); + PgColumn newColumn = newEntry.getValue(); + String newColumnName = newColumn.getName(); + char[] newColumnChars = newColumnName.toCharArray(); + HashMap findChars = new HashMap(); + for (int i = 0;i < oldColumnChars.length;++i) { + findChars.put(oldColumnChars[i], 0); + for (int j = 0;j < newColumnChars.length;++j) { + if (oldColumnChars[i] == newColumnChars[j] + && findChars.containsKey(newColumnChars[j]) + && 0 == findChars.get(newColumnChars[j])) { + findChars.put(newColumnChars[j], 1); + } + } + } + int currentContainsCount = 0; + for (Map.Entry findChar : findChars.entrySet()) { + currentContainsCount += findChar.getValue(); + } + if ((double)currentContainsCount >= (oldColumnName.length() + newColumnName.length()) / 4) { + if (containsCount < currentContainsCount) { + variants.remove(containsCount); + containsCount = currentContainsCount; + HashMap map = new HashMap(); + map.put(newColumnNumber, newColumn); + variants.put(containsCount, map); + } else if (containsCount == currentContainsCount) { + variants.get(containsCount).put(newColumnNumber, newColumn); + } + } + } + + for (Map.Entry> variantEntry : variants.entrySet()) { + HashMap map = variantEntry.getValue(); + + if (1 == map.size()) { + for (Map.Entry entry : map.entrySet()) { + PgColumn pgColumn = entry.getValue(); + this.columns.put(pgColumn.getName(), oldTable.getColumn(oldColumnName)); + } + } else { + for (Map.Entry entry : map.entrySet()) { + Integer pgColumnNumber = entry.getKey(); + PgColumn pgColumn = entry.getValue(); + if (oldColumnNumber == pgColumnNumber) { + this.columns.put(pgColumn.getName(), oldTable.getColumn(oldColumnName)); + } else { + int currentContainsCount = 0; + if (oldColumn.getNullValue() == pgColumn.getNullValue()) { + ++currentContainsCount; + } + if (oldColumn.getDefaultValue() == null ? pgColumn.getDefaultValue() == null : oldColumn.getDefaultValue().equals(pgColumn.getDefaultValue())) { + ++currentContainsCount; + } + if (oldColumn.getType() == null ? pgColumn.getType() == null : oldColumn.getType().equals(pgColumn.getType())) { + ++currentContainsCount; + } + if (3 == currentContainsCount) { + this.columns.put(pgColumn.getName(), oldTable.getColumn(oldColumnName)); + } + } + } + } + } + } + } + + public boolean hasNotColumn(String name) { + return null == this.getColumn(name); + } + + public PgColumn getColumn(String name) { + if (this.columns.containsKey(name)) { + return this.columns.get(name); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/resources/cz/startnet/utils/pgdiff/Resources.properties b/src/main/resources/cz/startnet/utils/pgdiff/Resources.properties index 9d3ad565..821009b7 100644 --- a/src/main/resources/cz/startnet/utils/pgdiff/Resources.properties +++ b/src/main/resources/cz/startnet/utils/pgdiff/Resources.properties @@ -48,6 +48,7 @@ NewDatabaseIgnoredStatements=New database ignored statements ErrorUnknownOption=ERROR: Unknown option WarningUnableToDetermineStorageType=WARNING: Column {0} in new table has no STORAGE set but in old table storage was set. Unable to determine STORAGE type. TypeParameterChange=TYPE change - table: {0} original: {1} new: {2} +RenameColumn=RENAME column - table: {0} original: {1} new: {2} UnsupportedEncoding=Unsupported encoding CannotReadFile=Cannot read file FileNotFound=File ''{0}'' not found diff --git a/src/test/java/cz/startnet/utils/pgdiff/PgDiffTest.java b/src/test/java/cz/startnet/utils/pgdiff/PgDiffTest.java index 45be0b3f..cd74861a 100644 --- a/src/test/java/cz/startnet/utils/pgdiff/PgDiffTest.java +++ b/src/test/java/cz/startnet/utils/pgdiff/PgDiffTest.java @@ -204,7 +204,9 @@ public static Collection parameters() { // Tests adding new sequence that is owned by table {"add_owned_sequence", false, true, false, false}, // Tests adding empty table - {"add_empty_table", false, false, false, false} + {"add_empty_table", false, false, false, false}, + // Tests rename column + {"modify_column_name", false, false, false, false} }); } /** diff --git a/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_diff.sql b/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_diff.sql new file mode 100644 index 00000000..87952fb4 --- /dev/null +++ b/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_diff.sql @@ -0,0 +1,7 @@ +ALTER TABLE testtable + DROP COLUMN meta_title, + RENAME COLUMN filed10 TO filed_11 /* RENAME column - table: testtable original: filed10 new: filed_11 */, + RENAME COLUMN field1 TO field_1 /* RENAME column - table: testtable original: field1 new: field_1 */, + RENAME COLUMN field2 TO field_21 /* RENAME column - table: testtable original: field2 new: field_21 */, + RENAME COLUMN meta_discription TO seo_description /* RENAME column - table: testtable original: meta_discription new: seo_description */, + RENAME COLUMN field_3 TO field3 /* RENAME column - table: testtable original: field_3 new: field3 */; \ No newline at end of file diff --git a/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_new.sql b/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_new.sql new file mode 100644 index 00000000..2d7d27fa --- /dev/null +++ b/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_new.sql @@ -0,0 +1,53 @@ +-- +-- PostgreSQL database dump +-- + +SET client_encoding = 'UTF8'; +SET check_function_bodies = false; +SET client_min_messages = warning; + +-- +-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: postgres +-- + +COMMENT ON SCHEMA public IS 'Standard public schema'; + + +SET search_path = public, pg_catalog; + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: testtable; Type: TABLE; Schema: public; Owner: fordfrog; Tablespace: +-- + +CREATE TABLE testtable ( + filed_11 character varying(100), + field_1 integer, + field_21 integer, + description text, + short_description text, + seo_description text, + field3 character varying(100) DEFAULT 'none'::character varying, + field4 double precision +); + + +ALTER TABLE public.testtable OWNER TO fordfrog; + +-- +-- Name: public; Type: ACL; Schema: -; Owner: postgres +-- + +REVOKE ALL ON SCHEMA public FROM PUBLIC; +REVOKE ALL ON SCHEMA public FROM postgres; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO PUBLIC; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_original.sql b/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_original.sql new file mode 100644 index 00000000..430e7230 --- /dev/null +++ b/src/test/resources/cz/startnet/utils/pgdiff/modify_column_name_original.sql @@ -0,0 +1,54 @@ +-- +-- PostgreSQL database dump +-- + +SET client_encoding = 'UTF8'; +SET check_function_bodies = false; +SET client_min_messages = warning; + +-- +-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: postgres +-- + +COMMENT ON SCHEMA public IS 'Standard public schema'; + + +SET search_path = public, pg_catalog; + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: testtable; Type: TABLE; Schema: public; Owner: fordfrog; Tablespace: +-- + +CREATE TABLE testtable ( + filed10 character varying(100), + field1 integer, + field2 integer, + meta_title text, + description text, + short_description text, + meta_discription text, + field_3 character varying(100) DEFAULT 'none'::character varying, + field4 double precision +); + + +ALTER TABLE public.testtable OWNER TO fordfrog; + +-- +-- Name: public; Type: ACL; Schema: -; Owner: postgres +-- + +REVOKE ALL ON SCHEMA public FROM PUBLIC; +REVOKE ALL ON SCHEMA public FROM postgres; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO PUBLIC; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/src/test/resources/cz/startnet/utils/pgdiff/modify_inherits_diff.sql b/src/test/resources/cz/startnet/utils/pgdiff/modify_inherits_diff.sql index 1fb87214..b6c6b97e 100644 --- a/src/test/resources/cz/startnet/utils/pgdiff/modify_inherits_diff.sql +++ b/src/test/resources/cz/startnet/utils/pgdiff/modify_inherits_diff.sql @@ -4,8 +4,9 @@ CREATE TABLE parenttable2 ( ); ALTER TABLE parenttable - DROP COLUMN id, - ADD COLUMN field3 information_schema.cardinal_number; + RENAME COLUMN id TO field3 /* RENAME column - table: parenttable original: id new: field3 */, + ALTER COLUMN field3 TYPE information_schema.cardinal_number /* TYPE change - table: parenttable original: bigserial new: information_schema.cardinal_number */, + ALTER COLUMN field3 DROP NOT NULL; ALTER TABLE testtable NO INHERIT parenttable;