|
| 1 | +package main |
| 2 | + |
| 3 | +import "fmt" |
| 4 | +import "database/sql" |
| 5 | +import "github.com/joncrlsn/pgutil" |
| 6 | + |
| 7 | +// IndexSchema holds a channel that streams index metadata from one of the databases. |
| 8 | +// It also holds a reference to the current row of data we're viewing. |
| 9 | +// |
| 10 | +// IndexSchema implements the Schema interface defined in pgdiff.go |
| 11 | +type IndexSchema struct { |
| 12 | + channel chan map[string]string |
| 13 | + row map[string]string |
| 14 | + done bool |
| 15 | +} |
| 16 | + |
| 17 | +// NextRow reads from the channel and tells you if there are (probably) more or not |
| 18 | +func (c *IndexSchema) NextRow() bool { |
| 19 | + c.row = <-c.channel |
| 20 | + if len(c.row) == 0 { |
| 21 | + c.done = true |
| 22 | + } |
| 23 | + return !c.done |
| 24 | +} |
| 25 | + |
| 26 | +// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row |
| 27 | +func (c *IndexSchema) Compare(obj interface{}) int { |
| 28 | + c2, ok := obj.(*IndexSchema) |
| 29 | + if !ok { |
| 30 | + fmt.Println("Error!!!, Change(...) needs a IndexSchema instance", c2) |
| 31 | + return +999 |
| 32 | + } |
| 33 | + |
| 34 | + //fmt.Printf("Comparing %s with %s", c.row["table_name"], c2.row["table_name"]) |
| 35 | + val := _compareString(c.row["table_name"], c2.row["table_name"]) |
| 36 | + if val != 0 { |
| 37 | + return val |
| 38 | + } |
| 39 | + |
| 40 | + val = _compareString(c.row["index_name"], c2.row["index_name"]) |
| 41 | + return val |
| 42 | +} |
| 43 | + |
| 44 | +// Add generates SQL to add the index |
| 45 | +func (c IndexSchema) Add() { |
| 46 | + uniqueStr := "" |
| 47 | + if c.row["unique"] == "true" { |
| 48 | + uniqueStr = "UNIQUE " |
| 49 | + } |
| 50 | + fmt.Printf("CREATE %sINDEX %s ON %s (%s);\n", uniqueStr, c.row["index_name"], c.row["table_name"], c.row["column_names"]) |
| 51 | +} |
| 52 | + |
| 53 | +// Drop generates SQL to drop the index |
| 54 | +func (c IndexSchema) Drop() { |
| 55 | + fmt.Printf("DROP INDEX %s;\n", c.row["index_name"]) |
| 56 | +} |
| 57 | + |
| 58 | +// Change handles the case where the table and index name match, but the details do not |
| 59 | +func (c IndexSchema) Change(obj interface{}) { |
| 60 | + c2, ok := obj.(*IndexSchema) |
| 61 | + if !ok { |
| 62 | + fmt.Println("Error!!!, change needs an IndexSchema instance", c2) |
| 63 | + } |
| 64 | + // No need to do anything, we either drop or add indices |
| 65 | +} |
| 66 | + |
| 67 | +/* |
| 68 | + * Compare the columns in the two databases |
| 69 | + */ |
| 70 | +func compareIndexes(conn1 *sql.DB, conn2 *sql.DB) { |
| 71 | + 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;` |
| 91 | + |
| 92 | + rowChan1, _ := pgutil.QueryStrings(conn1, sql) |
| 93 | + rowChan2, _ := pgutil.QueryStrings(conn2, sql) |
| 94 | + |
| 95 | + // We have to explicitly type this as Schema for some reason |
| 96 | + var schema1 Schema = &IndexSchema{channel: rowChan1} |
| 97 | + var schema2 Schema = &IndexSchema{channel: rowChan2} |
| 98 | + |
| 99 | + // Compare the columns |
| 100 | + doDiff(schema1, schema2) |
| 101 | +} |
0 commit comments