Skip to content

Commit 39cb7e8

Browse files
committed
WIP on index.go which incorporates unique and primary keys
1 parent 6d99fb3 commit 39cb7e8

3 files changed

Lines changed: 102 additions & 35 deletions

File tree

column.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (c ColumnSchema) Add() {
6767
// Drop returns SQL to drop the column
6868
func (c ColumnSchema) Drop() {
6969
// if dropping column
70-
fmt.Printf("ALTER TABLE %s DROP COLUMN %s;\n", c.row["table_name"], c.row["column_name"])
70+
fmt.Printf("ALTER TABLE %s DROP COLUMN IF EXISTS %s;\n", c.row["table_name"], c.row["column_name"])
7171
}
7272

7373
// Change handles the case where the table and column match, but the details do not

index.go

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,37 @@ func (c *IndexSchema) Compare(obj interface{}) int {
4141
return val
4242
}
4343

44-
// Add generates SQL to add the index
44+
// Add generates SQL to add the constraint/index
4545
func (c IndexSchema) Add() {
46-
uniqueStr := ""
47-
if c.row["unique"] == "true" {
48-
uniqueStr = "UNIQUE "
46+
47+
// Assertion
48+
if c.row["index_def"] == "null" {
49+
fmt.Printf("-- Unexpected situation in index.go: there is no index_def for %s %s\n", c.row["table_name"], c.row["index_name"])
50+
return
51+
}
52+
53+
// Create the index
54+
fmt.Println(c.row["index_def"])
55+
//fmt.Println("--", c.row["constraint_def"])
56+
57+
if c.row["constraint_def"] != "null" {
58+
// Create the constraint using the index we just created
59+
if c.row["pk"] == "true" {
60+
// Add primary key using the index
61+
fmt.Printf("ALTER TABLE IF EXISTS ONLY %s ADD CONSTRAINT %s PRIMARY KEY USING INDEX %s;\n", c.row["table_name"], c.row["index_name"], c.row["index_name"])
62+
} else if c.row["uq"] == "true" {
63+
// Add unique constraint using the index
64+
fmt.Printf("ALTER TABLE IF EXISTS ONLY %s ADD CONSTRAINT %s UNIQUE USING INDEX %s;\n", c.row["table_name"], c.row["index_name"], c.row["index_name"])
65+
}
4966
}
50-
fmt.Printf("CREATE %sINDEX %s ON %s (%s);\n", uniqueStr, c.row["index_name"], c.row["table_name"], c.row["column_names"])
5167
}
5268

53-
// Drop generates SQL to drop the index
69+
// Drop generates SQL to drop the index and/or the constraint related to it
5470
func (c IndexSchema) Drop() {
55-
fmt.Printf("DROP INDEX %s; -- %s ON (%s)\n", c.row["index_name"], c.row["table_name"], c.row["column_names"])
71+
if c.row["constraint_def"] != "null" {
72+
fmt.Printf("ALTER TABLE IF EXISTS ONLY %s DROP CONSTRAINT IF EXISTS %s; -- %s\n", c.row["table_name"], c.row["index_name"], c.row["constraint_def"])
73+
}
74+
fmt.Printf("DROP INDEX IF EXISTS %s; -- %s \n", c.row["index_name"], c.row["index_def"])
5675
}
5776

5877
// Change handles the case where the table and index name match, but the details do not
@@ -61,33 +80,86 @@ func (c IndexSchema) Change(obj interface{}) {
6180
if !ok {
6281
fmt.Println("Error!!!, change needs an IndexSchema instance", c2)
6382
}
64-
// No need to do anything, we either drop or add indices
83+
// Table and constraint name matches... We need to make sure the details match
84+
85+
// NOTE that there should always be an index_def for both c and c2 (but we're checking below anyway)
86+
if len(c.row["index_def"]) == 0 {
87+
fmt.Printf("-- Unexpected situation in index.go: index_def is empty for %v\n", c.row)
88+
return
89+
}
90+
if len(c2.row["index_def"]) == 0 {
91+
fmt.Printf("-- Unexpected situation in index.go: index_def is empty for %v\n", c2.row)
92+
return
93+
}
94+
95+
if c.row["constraint_def"] != c2.row["constraint_def"] {
96+
// c1.constraint and c2.constraint are just different
97+
if c.row["constraint_def"] == "null" {
98+
// c1.constraint does not exist, c2.constraint does, so
99+
// Drop constraint
100+
fmt.Printf("DROP INDEX IF EXISTS %s; -- %s \n", c2.row["index_name"], c2.row["index_def"])
101+
} else if c2.row["constraint_def"] == "null" {
102+
// c1.constraint exists, c2.constraint does not, so
103+
// Add constraint
104+
if c.row["index_def"] == c2.row["index_def"] {
105+
// Indexes match, so
106+
// Add constraint using the index
107+
if c.row["pk"] == "true" {
108+
// Add primary key using the index
109+
fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY USING INDEX %s;\n", c.row["table_name"], c.row["index_name"], c.row["index_name"])
110+
} else if c.row["uq"] == "true" {
111+
// Add unique constraint using the index
112+
fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE USING INDEX %s;\n", c.row["table_name"], c.row["index_name"], c.row["index_name"])
113+
} else {
114+
115+
}
116+
} else {
117+
// Drop the c2 index, create a copy of the c1 index
118+
fmt.Printf("DROP INDEX IF EXISTS %s; -- %s \n", c2.row["index_name"], c2.row["index_def"])
119+
120+
}
121+
fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s %s;\n", c.row["table_name"], c.row["constraint_name"], c.row["constraint_def"])
122+
123+
} else if c.row["index_def"] != c2.row["index_def"] {
124+
// The constraints match
125+
}
126+
127+
} else if c.row["index_def"] != c2.row["index_def"] {
128+
// Remember, if we are here, then the two constraint_defs match (both may be empty)
129+
// The indexes do not match, but the constraints do
130+
131+
// Drop the index (and maybe the constraint) so we can recreate the index
132+
c.Drop()
133+
134+
// Recreate the index (and a constraint if specified)
135+
c.Add()
136+
}
137+
65138
}
66139

67140
/*
68-
* Compare the columns in the two databases
141+
* Compare the indexes in the two databases
69142
*/
70143
func compareIndexes(conn1 *sql.DB, conn2 *sql.DB) {
144+
// This SQL was generated with psql -E -c "\d t_org"
145+
// The magic is in pg_get_indexdef and pg_get_constraint
71146
sql := `
72-
SELECT
73-
t.relname AS table_name,
74-
i.relname AS index_name,
75-
ix.indisunique AS unique,
76-
array_to_string(array_agg(a.attname), ', ') AS column_names
77-
FROM
78-
pg_index AS ix,
79-
pg_class AS t,
80-
pg_class AS i,
81-
pg_attribute AS a
82-
WHERE
83-
t.oid = ix.indrelid
84-
AND i.oid = ix.indexrelid
85-
AND a.attrelid = t.oid
86-
AND a.attnum = ANY(ix.indkey)
87-
AND t.relkind = 'r'
88-
AND t.relname not like 'pg_%'
89-
GROUP BY t.relname, i.relname, ix.indisunique
90-
ORDER BY t.relname, i.relname, ix.indisunique ASC;`
147+
SELECT c.relname AS table_name
148+
, c2.relname AS index_name
149+
, i.indisprimary AS pk
150+
, i.indisunique AS uq
151+
, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS index_def
152+
, pg_catalog.pg_get_constraintdef(con.oid, true) AS constraint_def
153+
, con.contype AS typ
154+
FROM pg_catalog.pg_index AS i
155+
JOIN pg_catalog.pg_class AS c ON (c.oid = i.indrelid)
156+
JOIN pg_catalog.pg_class AS c2 ON (c2.oid = i.indexrelid)
157+
LEFT JOIN pg_catalog.pg_constraint con
158+
ON (con.conrelid = i.indrelid AND con.conindid = i.indexrelid AND con.contype IN ('p','u','x'))
159+
WHERE c.relname NOT LIKE 'pg_%'
160+
--AND c.relname = 't_org'
161+
ORDER BY c.relname, c2.relname;
162+
`
91163

92164
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
93165
rowChan2, _ := pgutil.QueryStrings(conn2, sql)

pgdiff.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,14 @@ func main() {
6161
compareSequences(conn1, conn2)
6262
compareTables(conn1, conn2)
6363
compareColumns(conn1, conn2)
64-
comparePrimaryKeys(conn1, conn2)
65-
compareUniqueConstraints(conn1, conn2)
64+
compareIndexes(conn1, conn2)
6665
compareForeignKeys(conn1, conn2)
6766
} else if schemaType == "SEQUENCE" {
6867
compareSequences(conn1, conn2)
6968
} else if schemaType == "TABLE" {
7069
compareTables(conn1, conn2)
7170
} else if schemaType == "COLUMN" {
7271
compareColumns(conn1, conn2)
73-
} else if schemaType == "PRIMARY_KEY" {
74-
comparePrimaryKeys(conn1, conn2)
75-
} else if schemaType == "UNIQUE" {
76-
compareUniqueConstraints(conn1, conn2)
7772
} else if schemaType == "INDEX" {
7873
compareIndexes(conn1, conn2)
7974
} else if schemaType == "FOREIGN_KEY" {

0 commit comments

Comments
 (0)