11package main
22
3+ import "sort"
34import "fmt"
45import "strings"
56import "database/sql"
67import "github.com/joncrlsn/pgutil"
78
8- // IndexSchema holds a channel that streams index metadata from one of the databases.
9- // It also holds a reference to the current row of data we're viewing.
10- //
11- // IndexSchema implements the Schema interface defined in pgdiff.go
9+ // ==================================
10+ // IndexRows definition
11+ // ==================================
12+ type IndexRows []map [string ]string
13+
14+ func (slice IndexRows ) Len () int {
15+ return len (slice )
16+ }
17+
18+ func (slice IndexRows ) Less (i , j int ) bool {
19+ //fmt.Printf("--Less %s:%s with %s:%s", slice[i]["table_name"], slice[i]["column_name"], slice[j]["table_name"], slice[j]["column_name"])
20+ if slice [i ]["table_name" ] == slice [j ]["table_name" ] {
21+ return slice [i ]["index_name" ] < slice [j ]["index_name" ]
22+ }
23+ return slice [i ]["table_name" ] < slice [j ]["table_name" ]
24+ }
25+
26+ func (slice IndexRows ) Swap (i , j int ) {
27+ //fmt.Printf("--Swapping %d/%s:%s with %d/%s:%s \n", i, slice[i]["table_name"], slice[i]["index_name"], j, slice[j]["table_name"], slice[j]["index_name"])
28+ slice [i ], slice [j ] = slice [j ], slice [i ]
29+ }
30+
31+ // ==================================
32+ // IndexSchema definition
33+ // (implements Schema -- defined in pgdiff.go)
34+ // ==================================
35+
36+ // IndexSchema holds a slice of rows from one of the databases as well as
37+ // a reference to the current row of data we're viewing.
1238type IndexSchema struct {
13- channel chan map [string ]string
14- row map [string ]string
15- done bool
39+ rows IndexRows
40+ rowNum int
41+ done bool
42+ }
43+
44+ // get returns the value from the current row for the given key
45+ func (c * IndexSchema ) get (key string ) string {
46+ if c .rowNum >= len (c .rows ) {
47+ return ""
48+ }
49+ return c.rows [c.rowNum ][key ]
1650}
1751
18- // NextRow reads from the channel and tells you if there are (probably) more or not
52+ // get returns the current row for the given key
53+ func (c * IndexSchema ) getRow () map [string ]string {
54+ if c .rowNum >= len (c .rows ) {
55+ return make (map [string ]string )
56+ }
57+ return c .rows [c .rowNum ]
58+ }
59+
60+ // NextRow increments the rowNum and tells you whether or not there are more
1961func (c * IndexSchema ) NextRow () bool {
20- c .row = <- c .channel
21- if len (c .row ) == 0 {
62+ if c .rowNum >= len (c .rows )- 1 {
2263 c .done = true
2364 }
65+ c .rowNum = c .rowNum + 1
2466 return ! c .done
2567}
2668
2769// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
2870func (c * IndexSchema ) Compare (obj interface {}) int {
2971 c2 , ok := obj .(* IndexSchema )
3072 if ! ok {
31- fmt .Println ("Error!!!, Change(...) needs a IndexSchema instance" , c2 )
32- return + 999
73+ fmt .Println ("Error!!!, change needs a IndexSchema instance" , c2 )
3374 }
3475
35- //fmt.Printf("Comparing %s with %s", c.row["table_name"], c2.row["table_name"])
36- val := _compareString (c .row ["table_name" ], c2 .row ["table_name" ])
76+ if len (c .get ("table_name" )) == 0 || len (c .get ("index_name" )) == 0 {
77+ fmt .Printf ("--Comparing (table_name or index_name is empty): %v\n -- %v\n " , c .getRow (), c2 .getRow ())
78+ }
79+
80+ val := _compareString (c .get ("table_name" ), c2 .get ("table_name" ))
3781 if val != 0 {
82+ // Table name differed so return that value
3883 return val
3984 }
4085
41- val = _compareString (c .row ["index_name" ], c2 .row ["index_name" ])
86+ // Table name was the same so compare index name
87+ val = _compareString (c .get ("index_name" ), c2 .get ("index_name" ))
4288 return val
4389}
4490
45- // Add generates SQL to add the constraint/index
46- func (c IndexSchema ) Add () {
47- fmt .Println ("--\n --Add\n --" )
91+ // Add prints SQL to add the column
92+ func (c * IndexSchema ) Add () {
93+ // fmt.Println("--\n--Add\n--")
4894
4995 // Assertion
50- if c .row [ "index_def" ] == "null" {
51- fmt .Printf ("-- Unexpected situation in index.go: there is no index_def for %s %s\n " , c .row [ "table_name" ] , c .row [ "index_name" ] )
96+ if c .get ( "index_def" ) == "null" || len ( c . get ( "index_def" )) == 0 {
97+ fmt .Printf ("-- Add Unexpected situation in index.go: there is no index_def for %s %s\n " , c .get ( "table_name" ) , c .get ( "index_name" ) )
5298 return
5399 }
54100
55- // Create the index
56- fmt .Printf ("%s;\n " , c .row [ "index_def" ] )
101+ // Create the index first
102+ fmt .Printf ("%s;\n " , c .get ( "index_def" ) )
57103
58- if c .row [ "constraint_def" ] != "null" {
104+ if c .get ( "constraint_def" ) != "null" {
59105 // Create the constraint using the index we just created
60- if c .row [ "pk" ] == "true" {
106+ if c .get ( "pk" ) == "true" {
61107 // Add primary key using the index
62- fmt .Printf ("ALTER TABLE ONLY %s ADD CONSTRAINT %s PRIMARY KEY USING INDEX %s;\n " , c .row [ "table_name" ] , c .row [ "index_name" ] , c .row [ "index_name" ] )
63- } else if c .row [ "uq" ] == "true" {
108+ fmt .Printf ("ALTER TABLE ONLY %s ADD CONSTRAINT %s PRIMARY KEY USING INDEX %s;\n " , c .get ( "table_name" ) , c .get ( "index_name" ) , c .get ( "index_name" ) )
109+ } else if c .get ( "uq" ) == "true" {
64110 // Add unique constraint using the index
65- fmt .Printf ("ALTER TABLE ONLY %s ADD CONSTRAINT %s UNIQUE USING INDEX %s;\n " , c .row [ "table_name" ] , c .row [ "index_name" ] , c .row [ "index_name" ] )
111+ fmt .Printf ("ALTER TABLE ONLY %s ADD CONSTRAINT %s UNIQUE USING INDEX %s;\n " , c .get ( "table_name" ) , c .get ( "index_name" ) , c .get ( "index_name" ) )
66112 }
67113 }
68114}
69115
70- // Drop generates SQL to drop the index and/or the constraint related to it
71- func (c IndexSchema ) Drop () {
72- fmt .Println ("--\n --Drop\n --" )
73- if c .row [ "constraint_def" ] != "null" {
116+ // Drop prints SQL to drop the column
117+ func (c * IndexSchema ) Drop () {
118+ // fmt.Println("--\n--Drop\n--")
119+ if c .get ( "constraint_def" ) != "null" {
74120 fmt .Println ("-- Warning, this may drop foreign keys pointing at this column. Make sure you re-run the FOREIGN_KEY diff after running this SQL." )
75- //fmt.Printf("ALTER TABLE ONLY %s DROP CONSTRAINT IF EXISTS %s CASCADE; -- %s\n", c.row[ "table_name"] , c.row[ "index_name"] , c.row[ "constraint_def"] )
76- fmt .Printf ("ALTER TABLE ONLY %s DROP CONSTRAINT IF EXISTS %s CASCADE;\n " , c .row [ "table_name" ] , c .row [ "index_name" ] )
121+ //fmt.Printf("ALTER TABLE ONLY %s DROP CONSTRAINT IF EXISTS %s CASCADE; -- %s\n", c.get( "table_name") , c.get( "index_name") , c.get( "constraint_def") )
122+ fmt .Printf ("ALTER TABLE ONLY %s DROP CONSTRAINT IF EXISTS %s CASCADE;\n " , c .get ( "table_name" ) , c .get ( "index_name" ) )
77123 }
78124 // The second line has no index_def
79- //fmt.Printf("DROP INDEX IF EXISTS %s; -- %s \n", c.row[ "index_name"] , c.row[ "index_def"] )
80- fmt .Printf ("DROP INDEX IF EXISTS %s;\n " , c .row [ "index_name" ] )
125+ //fmt.Printf("DROP INDEX IF EXISTS %s; -- %s \n", c.get( "index_name") , c.get( "index_def") )
126+ fmt .Printf ("DROP INDEX IF EXISTS %s;\n " , c .get ( "index_name" ) )
81127}
82128
83- // Change handles the case where the table and index name match, but the details do not
84- func (c IndexSchema ) Change (obj interface {}) {
129+ // Change handles the case where the table and column match, but the details do not
130+ func (c * IndexSchema ) Change (obj interface {}) {
85131 c2 , ok := obj .(* IndexSchema )
86132 if ! ok {
87133 fmt .Println ("-- Error!!!, change needs an IndexSchema instance" , c2 )
88134 }
89135 // Table and constraint name matches... We need to make sure the details match
90136
91137 // NOTE that there should always be an index_def for both c and c2 (but we're checking below anyway)
92- if len (c .row [ "index_def" ] ) == 0 {
93- fmt .Printf ("-- Unexpected situation in index.go: index_def is empty for %v \n " , c .row )
138+ if len (c .get ( "index_def" ) ) == 0 {
139+ fmt .Printf ("-- Change: Unexpected situation in index.go: index_def is empty for 1: %v 2:%v \n " , c .getRow (), c2 . getRow () )
94140 return
95141 }
96- if len (c2 .row [ "index_def" ] ) == 0 {
97- fmt .Printf ("-- Unexpected situation in index.go: index_def is empty for %v \n " , c2 .row )
142+ if len (c2 .get ( "index_def" ) ) == 0 {
143+ fmt .Printf ("-- Change: Unexpected situation in index.go: index_def is empty for 2: %v 1: %v \n " , c2 .getRow (), c . getRow () )
98144 return
99145 }
100146
101- if c .row ["constraint_def" ] != c2 .row ["constraint_def" ] {
102- fmt .Println ("--\n --CHANGE: constraint defs different\n --" )
147+ if c .get ("constraint_def" ) != c2 .get ("constraint_def" ) {
103148 // c1.constraint and c2.constraint are just different
104- fmt .Printf ("-- Different defs:\n -- %s\n -- %s\n " , c .row [ "constraint_def" ] , c2 .row [ "constraint_def" ] )
105- if c .row [ "constraint_def" ] == "null" {
149+ fmt .Printf ("-- CHANGE: Different defs:\n -- %s\n -- %s\n " , c .get ( "constraint_def" ) , c2 .get ( "constraint_def" ) )
150+ if c .get ( "constraint_def" ) == "null" {
106151 // c1.constraint does not exist, c2.constraint does, so
107152 // Drop constraint
108- fmt .Printf ("DROP INDEX IF EXISTS %s; -- %s \n " , c2 .row [ "index_name" ] , c2 .row [ "index_def" ] )
109- } else if c2 .row [ "constraint_def" ] == "null" {
153+ fmt .Printf ("DROP INDEX IF EXISTS %s; -- %s \n " , c2 .get ( "index_name" ) , c2 .get ( "index_def" ) )
154+ } else if c2 .get ( "constraint_def" ) == "null" {
110155 // c1.constraint exists, c2.constraint does not, so
111156 // Add constraint
112- if c .row [ "index_def" ] == c2 .row [ "index_def" ] {
157+ if c .get ( "index_def" ) == c2 .get ( "index_def" ) {
113158 // Indexes match, so
114159 // Add constraint using the index
115- if c .row [ "pk" ] == "true" {
160+ if c .get ( "pk" ) == "true" {
116161 // Add primary key using the index
117- 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" ] )
118- } else if c .row [ "uq" ] == "true" {
162+ fmt .Printf ("ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY USING INDEX %s;\n " , c .get ( "table_name" ) , c .get ( "index_name" ) , c .get ( "index_name" ) )
163+ } else if c .get ( "uq" ) == "true" {
119164 // Add unique constraint using the index
120- 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" ] )
165+ fmt .Printf ("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE USING INDEX %s;\n " , c .get ( "table_name" ) , c .get ( "index_name" ) , c .get ( "index_name" ) )
121166 } else {
122167
123168 }
124169 } else {
125170 // Drop the c2 index, create a copy of the c1 index
126- fmt .Printf ("DROP INDEX IF EXISTS %s; -- %s \n " , c2 .row [ "index_name" ] , c2 .row [ "index_def" ] )
171+ fmt .Printf ("DROP INDEX IF EXISTS %s; -- %s \n " , c2 .get ( "index_name" ) , c2 .get ( "index_def" ) )
127172 }
128173 // WIP
129- //fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s %s;\n", c.row[ "table_name"] , c.row[ "index_name"] , c.row[ "constraint_def"] )
174+ //fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s %s;\n", c.get( "table_name") , c.get( "index_name") , c.get( "constraint_def") )
130175
131- } else if c .row [ "index_def" ] != c2 .row [ "index_def" ] {
176+ } else if c .get ( "index_def" ) != c2 .get ( "index_def" ) {
132177 // The constraints match
133178 }
134-
135- } else if c .row ["index_def" ] != c2 .row ["index_def" ] {
136- if ! strings .HasPrefix (c .row ["index_def" ], c2 .row ["index_def" ]) &&
137- ! strings .HasPrefix (c2 .row ["index_def" ], c .row ["index_def" ]) {
179+ } else if c .get ("index_def" ) != c2 .get ("index_def" ) {
180+ if ! strings .HasPrefix (c .get ("index_def" ), c2 .get ("index_def" )) &&
181+ ! strings .HasPrefix (c2 .get ("index_def" ), c .get ("index_def" )) {
138182 fmt .Println ("--\n --Change index defs different\n --" )
139183 // Remember, if we are here, then the two constraint_defs match (both may be empty)
140184 // The indexes do not match, but the constraints do
141- fmt .Printf ("--CHANGE: Different index defs:\n -- %s\n -- %s\n " , c .row [ "index_def" ] , c2 .row [ "index_def" ] )
185+ fmt .Printf ("--CHANGE: Different index defs:\n -- %s\n -- %s\n " , c .get ( "index_def" ) , c2 .get ( "index_def" ) )
142186
143187 // Drop the index (and maybe the constraint) so we can recreate the index
144188 c .Drop ()
@@ -150,12 +194,16 @@ func (c IndexSchema) Change(obj interface{}) {
150194
151195}
152196
197+ // ==================================
198+ // Functions
199+ // ==================================
200+
153201/*
154- * Compare the indexes in the two databases
202+ * Compare the columns in the two databases
155203 */
156204func compareIndexes (conn1 * sql.DB , conn2 * sql.DB ) {
157205 // This SQL was generated with psql -E -c "\d t_org"
158- // The magic is in pg_get_indexdef and pg_get_constraint
206+ // The " magic" is in pg_get_indexdef and pg_get_constraint
159207 sql := `
160208SELECT c.relname AS table_name
161209 , c2.relname AS index_name
@@ -166,20 +214,31 @@ SELECT c.relname AS table_name
166214 , con.contype AS typ
167215FROM pg_catalog.pg_index AS i
168216JOIN pg_catalog.pg_class AS c ON (c.oid = i.indrelid)
169- JOIN pg_catalog.pg_class AS c2 ON (c2.oid = i.indexrelid)
170- LEFT JOIN pg_catalog.pg_constraint con
217+ JOIN pg_catalog.pg_class AS c2 ON (c2.oid = i.indexrelid)
218+ LEFT JOIN pg_catalog.pg_constraint con
171219 ON (con.conrelid = i.indrelid AND con.conindid = i.indexrelid AND con.contype IN ('p','u','x'))
172220WHERE c.relname NOT LIKE 'pg_%'
173- AND c.relname = 't_org'
174- ORDER BY c.relname, c2.relname;
221+ -- AND c.relname = 't_org'
222+ -- ORDER BY c.relname, c2.relname;
175223`
176-
177224 rowChan1 , _ := pgutil .QueryStrings (conn1 , sql )
178225 rowChan2 , _ := pgutil .QueryStrings (conn2 , sql )
179226
180- // We have to explicitly type this as Schema for some reason
181- var schema1 Schema = & IndexSchema {channel : rowChan1 }
182- var schema2 Schema = & IndexSchema {channel : rowChan2 }
227+ rows1 := make (IndexRows , 0 )
228+ for row := range rowChan1 {
229+ rows1 = append (rows1 , row )
230+ }
231+ sort .Sort (rows1 )
232+
233+ rows2 := make (IndexRows , 0 )
234+ for row := range rowChan2 {
235+ rows2 = append (rows2 , row )
236+ }
237+ sort .Sort (rows2 )
238+
239+ // We have to explicitly type this as Schema here for some unknown reason
240+ var schema1 Schema = & IndexSchema {rows : rows1 , rowNum : - 1 }
241+ var schema2 Schema = & IndexSchema {rows : rows2 , rowNum : - 1 }
183242
184243 // Compare the columns
185244 doDiff (schema1 , schema2 )
0 commit comments