Skip to content

Commit 8e89bf9

Browse files
committed
Refactoring to keep pgdiff.go small and grokable
1 parent 28501ab commit 8e89bf9

4 files changed

Lines changed: 168 additions & 161 deletions

File tree

column.go

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package main
22

33
import "fmt"
4-
4+
import "database/sql"
5+
import "github.com/joncrlsn/pgutil"
56

67
// ColumnSchema holds a channel streaming column data from one of the databases as well as
78
// a reference to the current row of data we're viewing.
@@ -10,15 +11,16 @@ import "fmt"
1011
type ColumnSchema struct {
1112
channel chan map[string]string
1213
row map[string]string
14+
done bool
1315
}
1416

15-
// NextRow reads from the channel and tells you whether or not there might be more rows
16-
func (c *ColumnSchema) NextRow(more bool) bool {
17+
// NextRow reads from the channel and tells you if there are (probably) more or not
18+
func (c *ColumnSchema) NextRow() bool {
1719
c.row = <-c.channel
18-
if !more || len(c.row) == 0 {
19-
return false
20+
if len(c.row) == 0 {
21+
c.done = true
2022
}
21-
return true
23+
return !c.done
2224
}
2325

2426
// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
@@ -68,7 +70,43 @@ func (c ColumnSchema) Change(obj interface{}) {
6870
if !ok {
6971
fmt.Println("Error!!!, change needs a ColumnSchema instance", c2)
7072
}
71-
// fmt.Printf("Changes? ")
73+
74+
// Detect column type change (mostly varchar length, or number size increase) (integer to/from bigint is OK)
75+
if c.row["data_type"] == c2.row["data_type"] {
76+
if c.row["data_type"] == "character varying" {
77+
if c.row["character_maximum_length"] != c2.row["character_maximum_length"] {
78+
if c.row["character_maximum_length"] < c2.row["character_maximum_length"] {
79+
fmt.Println("-- WARNING: The next statement will shorten a character varying column.")
80+
}
81+
fmt.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE character varying(%s);\n", c.row["table_name"], c.row["column_name"], c.row["character_maximum_length"])
82+
}
83+
}
84+
}
85+
86+
// TODO: Code and test a column change from integer to bigint
87+
if c.row["data_type"] != c2.row["data_type"] {
88+
fmt.Printf("-- WARNING: This program does not (yet) handle type changes (%s to %s).\n", c2.row["data_type"], c.row["data_type"])
89+
}
90+
91+
// Detect column default change (or added, dropped)
92+
if c.row["column_default"] == "null" {
93+
if c.row["column_default"] != "null" {
94+
fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;\n", c.row["table_name"], c.row["column_name"])
95+
}
96+
} else if c.row["column_default"] != c2.row["column_default"] {
97+
fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DEFAULT %s;\n", c.row["table_name"], c.row["column_name"], c.row["column_default"])
98+
}
99+
100+
if c.row["is_nullable"] != c2.row["is_nullable"] {
101+
if c.row["is_nullable"] == "YES" {
102+
fmt.Printf("ALTER TABLE %s ALTER COLUMN DROP NOT NULL")
103+
} else {
104+
fmt.Printf("ALTER TABLE %s ALTER COLUMN SET NOT NULL")
105+
}
106+
}
107+
108+
// TODO Detect not-null and nullable change
109+
72110
// // if changing type
73111
// if c.row["data_type"] == "character varying" {
74112
// // varchar needs a length specified
@@ -90,3 +128,29 @@ func (c ColumnSchema) Change(obj interface{}) {
90128
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL;\n", c.row["table_name"], c.row["column_name"])
91129
// return "Change"
92130
}
131+
132+
/*
133+
* Compare the columns in the two databases
134+
*/
135+
func compareColumns(conn1 *sql.DB, conn2 *sql.DB) {
136+
sql := `
137+
SELECT table_name
138+
, column_name
139+
, data_type
140+
, is_nullable
141+
, column_default
142+
, character_maximum_length
143+
FROM information_schema.columns
144+
WHERE table_schema = 'public'
145+
ORDER by table_name, column_name;`
146+
147+
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
148+
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
149+
150+
// We have to explicitly type this as Schema for some reason
151+
var schema1 Schema = &ColumnSchema{channel: rowChan1}
152+
var schema2 Schema = &ColumnSchema{channel: rowChan2}
153+
154+
// Compare the columns
155+
doDiff(schema1, schema2)
156+
}

foreignkey.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
package main
22

33
import "fmt"
4+
import "database/sql"
5+
import "github.com/joncrlsn/pgutil"
46

5-
// ForeignKeySchema holds a channel streaming foreign key data from one of the databases as well as
7+
// ForeignKeySchema holds a channel streaming foreign key data from one of the databases as well as
68
// a reference to the current row of data we're viewing.
79
//
810
// ForeignKeySchema implements the Schema interface defined in pgdiff.go
911
type ForeignKeySchema struct {
1012
channel chan map[string]string
1113
row map[string]string
14+
done bool
1215
}
1316

14-
// NextRow reads from the channel and tells you if you are at the end or not
15-
func (c *ForeignKeySchema) NextRow(more bool) bool {
17+
// NextRow reads from the channel and tells you if there are (probably) more or not
18+
func (c *ForeignKeySchema) NextRow() bool {
1619
c.row = <-c.channel
17-
//fmt.Println("Found ", c.row["table_name"])
18-
19-
if !more || len(c.row) == 0 {
20-
return false
20+
if len(c.row) == 0 {
21+
c.done = true
2122
}
22-
return true
23+
return !c.done
2324
}
2425

2526
// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
@@ -58,3 +59,33 @@ func (c ForeignKeySchema) Change(obj interface{}) {
5859
}
5960
//fmt.Printf("Change Table? %s - %s\n", c.row["table_name"], c2.row["table_name"])
6061
}
62+
63+
/*
64+
* Compare the columns in the two databases
65+
*/
66+
func compareForeignKeys(conn1 *sql.DB, conn2 *sql.DB) {
67+
sql := `
68+
SELECT tc.constraint_name
69+
, tc.table_name
70+
, kcu.column_name
71+
, ccu.table_name AS foreign_table_name
72+
, ccu.column_name AS foreign_column_name
73+
FROM
74+
information_schema.table_constraints AS tc
75+
JOIN information_schema.key_column_usage AS kcu
76+
ON tc.constraint_name = kcu.constraint_name
77+
JOIN information_schema.constraint_column_usage AS ccu
78+
ON ccu.constraint_name = tc.constraint_name
79+
WHERE constraint_type = 'FOREIGN KEY'
80+
ORDER BY tc.table_name, tc.constraint_name; `
81+
82+
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
83+
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
84+
85+
// We have to explicitly type this as Schema for some reason
86+
var schema1 Schema = &ForeignKeySchema{channel: rowChan1}
87+
var schema2 Schema = &ForeignKeySchema{channel: rowChan2}
88+
89+
// Compare the columns
90+
doDiff(schema1, schema2)
91+
}

0 commit comments

Comments
 (0)