Skip to content

Commit 28501ab

Browse files
committed
formatted with fmt and performed golint suggestions
1 parent 52fa75e commit 28501ab

6 files changed

Lines changed: 117 additions & 106 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# pgdiff - PostgreSQL schema diff
22

3-
Written in GoLang, this utility compares the schema between two PostgreSQL databases and generates alter statements to be run against the second database. Not everything in the schema is compared, but the thinngs I considered important are: tables, columns (and their default values), foreign keys, and (soon) constraints and user roles.
3+
Written in GoLang, this utility compares the schema between two PostgreSQL databases and generates alter statements to be *manually* run against the second database. Not everything in the schema is compared, but the things considered important (at the moment) are: tables, columns (and their default values), foreign keys... and soon constraints and user roles.
44

5-
It is written to be easy to add and improve the accuracy of the comparison. Please let me know if you think this goal has not been met. I'm very interested in suggestions and contributions to improve this program. I'm not a GoLang expert yet, but each program I write gets me closer to that goal.
65

7-
I'm a big fan of GoLang because of how easy it is to deliver a single executable on almost any platform. But, just as important I love the design choices and the concurrency features which I've only begun to delve into. Streaming objects back (via a channel) from a method one by one is far better than returning a potentially massive list of objects.
6+
It is written to be easy to add and improve the accuracy of the diff. Please let me know if you think this goal has not been met. I'm very interested in suggestions and contributions to improve this program. I'm not a GoLang expert yet, but each program I write gets me closer to that goal.
87

9-
A couple of binaries to save you the effort:
10-
[Mac](https://github.com/joncrlsn/pgdiff/raw/master/bin-osx/pgdiff "OSX version")
8+
I'm a big fan of GoLang because of how easy it is to deliver a single executable on almost any platform. But, just as important I love the design choices and the concurrency features which I've only begun to delve into. Streaming objects back (via a channel) from a go routine is far better than returning a potentially massive list of objects.
9+
10+
<-- A couple of binaries to save you the effort: [Mac](https://github.com/joncrlsn/pgdiff/raw/master/bin-osx/pgdiff "OSX version") -->
1111

1212
## usage
1313

column.go

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,26 @@ package main
22

33
import "fmt"
44

5+
6+
// ColumnSchema holds a channel streaming column data from one of the databases as well as
7+
// a reference to the current row of data we're viewing.
8+
//
9+
// ColumnSchema implements the Schema interface defined in pgdiff.go
510
type ColumnSchema struct {
611
channel chan map[string]string
712
row map[string]string
813
}
914

10-
// Reads from the channel and converts the end-of-channel value into a boolean
15+
// NextRow reads from the channel and tells you whether or not there might be more rows
1116
func (c *ColumnSchema) NextRow(more bool) bool {
1217
c.row = <-c.channel
1318
if !more || len(c.row) == 0 {
14-
return false
19+
return false
1520
}
1621
return true
1722
}
1823

19-
// Compare
24+
// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
2025
func (c ColumnSchema) Compare(obj interface{}) int {
2126
c2, ok := obj.(*ColumnSchema)
2227
if !ok {
@@ -34,7 +39,7 @@ func (c ColumnSchema) Compare(obj interface{}) int {
3439
return val
3540
}
3641

37-
// Return SQL to add the column
42+
// Add returns SQL to add the column
3843
func (c ColumnSchema) Add() {
3944
if c.row["data_type"] == "character varying" {
4045
fmt.Printf("ALTER TABLE %s ADD COLUMN %s %s(%s)", c.row["table_name"], c.row["column_name"], c.row["data_type"], c.row["character_maximum_length"])
@@ -51,37 +56,37 @@ func (c ColumnSchema) Add() {
5156
fmt.Printf(";\n")
5257
}
5358

54-
// Return SQL to drop the column
59+
// Drop returns SQL to drop the column
5560
func (c ColumnSchema) Drop() {
5661
// if dropping column
57-
fmt.Printf("ALTER TABLE %s DROP COLUMN %s;\n", c.row["table_name"], c.row["column_name"])
62+
fmt.Printf("ALTER TABLE %s DROP COLUMN %s;\n", c.row["table_name"], c.row["column_name"])
5863
}
5964

60-
// Handle the case where the table and column match, but the details do not
65+
// Change handles the case where the table and column match, but the details do not
6166
func (c ColumnSchema) Change(obj interface{}) {
6267
c2, ok := obj.(*ColumnSchema)
6368
if !ok {
6469
fmt.Println("Error!!!, change needs a ColumnSchema instance", c2)
6570
}
66-
// fmt.Printf("Changes? ")
67-
// // if changing type
68-
// if c.row["data_type"] == "character varying" {
69-
// // varchar needs a length specified
70-
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE %s(%s);\n", c.row["table_name"], c.row["column_name"], c.row["data_type"], c.row["character_maximum_length"])
71-
// } else {
72-
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE %s;\n", c.row["table_name"], c.row["column_name"], c.row["data_type"])
73-
// }
74-
//
75-
// // if changing/adding default value
76-
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s;\n", c.row["table_name"], c.row["column_name"], c.row["column_default"])
77-
//
78-
// // if dropping default value
79-
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;\n", c.row["table_name"], c.row["column_name"])
80-
//
81-
// // if adding not null
82-
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s SET NOT NULL;\n", c.row["table_name"], c.row["column_name"])
83-
//
84-
// // if dropping not null
85-
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL;\n", c.row["table_name"], c.row["column_name"])
86-
// return "Change"
71+
// fmt.Printf("Changes? ")
72+
// // if changing type
73+
// if c.row["data_type"] == "character varying" {
74+
// // varchar needs a length specified
75+
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE %s(%s);\n", c.row["table_name"], c.row["column_name"], c.row["data_type"], c.row["character_maximum_length"])
76+
// } else {
77+
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE %s;\n", c.row["table_name"], c.row["column_name"], c.row["data_type"])
78+
// }
79+
//
80+
// // if changing/adding default value
81+
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s;\n", c.row["table_name"], c.row["column_name"], c.row["column_default"])
82+
//
83+
// // if dropping default value
84+
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;\n", c.row["table_name"], c.row["column_name"])
85+
//
86+
// // if adding not null
87+
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s SET NOT NULL;\n", c.row["table_name"], c.row["column_name"])
88+
//
89+
// // if dropping not null
90+
// fmt.Printf("ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL;\n", c.row["table_name"], c.row["column_name"])
91+
// return "Change"
8792
}

flags.go

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,27 @@ package main
33
import "flag"
44
import "github.com/joncrlsn/pgutil"
55

6-
func ParseFlags() (pgutil.DbInfo, pgutil.DbInfo) {
6+
func parseFlags() (pgutil.DbInfo, pgutil.DbInfo) {
77

8-
var dbUser1 = flag.String("U1", "", "db user")
9-
var dbPass1 = flag.String("pw1", "", "db password")
10-
var dbHost1 = flag.String("h1", "localhost", "db host")
11-
var dbPort1 = flag.Int("p1", 5432, "db port")
12-
var dbName1 = flag.String("d1", "", "db name")
13-
var dbOptions1 = flag.String("o1", "", "db options (eg. sslmode=disable)")
14-
15-
var dbUser2 = flag.String("U2", "", "db user")
16-
var dbPass2 = flag.String("pw2", "", "db password")
17-
var dbHost2 = flag.String("h2", "localhost", "db host")
18-
var dbPort2 = flag.Int("p2", 5432, "db port")
19-
var dbName2 = flag.String("d2", "", "db name")
20-
var dbOptions2 = flag.String("o2", "", "db options (eg. sslmode=disable)")
8+
var dbUser1 = flag.String("U1", "", "db user")
9+
var dbPass1 = flag.String("pw1", "", "db password")
10+
var dbHost1 = flag.String("h1", "localhost", "db host")
11+
var dbPort1 = flag.Int("p1", 5432, "db port")
12+
var dbName1 = flag.String("d1", "", "db name")
13+
var dbOptions1 = flag.String("o1", "", "db options (eg. sslmode=disable)")
2114

22-
flag.Parse()
15+
var dbUser2 = flag.String("U2", "", "db user")
16+
var dbPass2 = flag.String("pw2", "", "db password")
17+
var dbHost2 = flag.String("h2", "localhost", "db host")
18+
var dbPort2 = flag.Int("p2", 5432, "db port")
19+
var dbName2 = flag.String("d2", "", "db name")
20+
var dbOptions2 = flag.String("o2", "", "db options (eg. sslmode=disable)")
2321

24-
dbInfo1 := pgutil.DbInfo{DbName:*dbName1, DbHost:*dbHost1, DbPort:int32(*dbPort1), DbUser:*dbUser1, DbPass:*dbPass1, DbOptions:*dbOptions1}
22+
flag.Parse()
2523

26-
dbInfo2 := pgutil.DbInfo{DbName:*dbName2, DbHost:*dbHost2, DbPort:int32(*dbPort2), DbUser:*dbUser2, DbPass:*dbPass2, DbOptions:*dbOptions2}
24+
dbInfo1 := pgutil.DbInfo{DbName: *dbName1, DbHost: *dbHost1, DbPort: int32(*dbPort1), DbUser: *dbUser1, DbPass: *dbPass1, DbOptions: *dbOptions1}
2725

28-
return dbInfo1, dbInfo2
29-
}
26+
dbInfo2 := pgutil.DbInfo{DbName: *dbName2, DbHost: *dbHost2, DbPort: int32(*dbPort2), DbUser: *dbUser2, DbPass: *dbPass2, DbOptions: *dbOptions2}
3027

28+
return dbInfo1, dbInfo2
29+
}

foreignkey.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,59 @@ package main
22

33
import "fmt"
44

5+
// ForeignKeySchema holds a channel streaming foreign key data from one of the databases as well as
6+
// a reference to the current row of data we're viewing.
7+
//
8+
// ForeignKeySchema implements the Schema interface defined in pgdiff.go
59
type ForeignKeySchema struct {
610
channel chan map[string]string
711
row map[string]string
812
}
913

10-
// Reads from the channel and converts the end-of-channel value into a boolean
14+
// NextRow reads from the channel and tells you if you are at the end or not
1115
func (c *ForeignKeySchema) NextRow(more bool) bool {
1216
c.row = <-c.channel
13-
//fmt.Println("Found ", c.row["table_name"])
17+
//fmt.Println("Found ", c.row["table_name"])
1418

1519
if !more || len(c.row) == 0 {
1620
return false
1721
}
1822
return true
1923
}
2024

21-
// Compare
25+
// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
2226
func (c *ForeignKeySchema) Compare(obj interface{}) int {
2327
c2, ok := obj.(*ForeignKeySchema)
2428
if !ok {
2529
fmt.Println("Error!!!, Change(...) needs a ForeignKeySchema instance", c2)
26-
return +999
30+
return +999
2731
}
2832

2933
//fmt.Printf("Comparing %s with %s", c.row["table_name"], c2.row["table_name"])
3034
val := _compareString(c.row["table_name"], c2.row["table_name"])
31-
if val != 0 {
32-
return val
33-
}
35+
if val != 0 {
36+
return val
37+
}
3438

3539
val = _compareString(c.row["constraint_name"], c2.row["constraint_name"])
3640
return val
3741
}
3842

39-
// Return SQL to add the table
43+
// Add returns SQL to add the foreign key
4044
func (c ForeignKeySchema) Add() {
41-
fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY(%s) REFERENCES %s(%s);\n", c.row["table_name"], c.row["constraint_name"], c.row["column_name"], c.row["foreign_table_name"], c.row["foreign_column_name"], )
45+
fmt.Printf("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY(%s) REFERENCES %s(%s);\n", c.row["table_name"], c.row["constraint_name"], c.row["column_name"], c.row["foreign_table_name"], c.row["foreign_column_name"])
4246
}
4347

44-
// Return SQL to drop the table
48+
// Drop returns SQL to drop the foreign key
4549
func (c ForeignKeySchema) Drop() {
46-
fmt.Printf("ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s;\n", c.row["table_name"], c.row["constraint_name"])
50+
fmt.Printf("ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s;\n", c.row["table_name"], c.row["constraint_name"])
4751
}
4852

49-
// Handle the case where the table and column match, but the details do not
53+
// Change handles the case where the table and foreign key name, but the details do not
5054
func (c ForeignKeySchema) Change(obj interface{}) {
5155
c2, ok := obj.(*ForeignKeySchema)
5256
if !ok {
5357
fmt.Println("Error!!!, change needs a ForeignKeySchema instance", c2)
5458
}
55-
//fmt.Printf("Change Table? %s - %s\n", c.row["table_name"], c2.row["table_name"])
59+
//fmt.Printf("Change Table? %s - %s\n", c.row["table_name"], c2.row["table_name"])
5660
}

pgdiff.go

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import "database/sql"
1111
import _ "github.com/lib/pq"
1212
import "github.com/joncrlsn/pgutil"
1313

14-
// A database definition (table, column, constraint, indes, role, etc) that can be
14+
15+
// Schema is a database definition (table, column, constraint, indes, role, etc) that can be
1516
// added, dropped, or changed to match another database.
1617
type Schema interface {
1718
Compare(schema interface{}) int
@@ -26,43 +27,43 @@ type Schema interface {
2627
*/
2728
func main() {
2829
//testDiff()
29-
//os.Exit(0)
30+
//os.Exit(0)
3031

31-
dbInfo1, dbInfo2 := ParseFlags()
32+
dbInfo1, dbInfo2 := parseFlags()
3233
fmt.Println("-- db1:", dbInfo1)
3334
fmt.Println("-- db2:", dbInfo2)
34-
fmt.Println("Run the following SQL againt db2")
35+
fmt.Println("Run the following SQL againt db2")
3536

3637
// Remaining args:
3738
args := flag.Args()
38-
if len(args) == 0 {
39-
fmt.Println("The required first argument is SchemaType: TABLE, COLUMN, FOREIGN_KEY, CONSTRAINT, ROLE")
40-
os.Exit(1)
41-
}
39+
if len(args) == 0 {
40+
fmt.Println("The required first argument is SchemaType: TABLE, COLUMN, FOREIGN_KEY, CONSTRAINT, ROLE")
41+
os.Exit(1)
42+
}
4243

4344
conn1, err := dbInfo1.Open()
4445
check("opening database", err)
4546

4647
conn2, err := dbInfo2.Open()
4748
check("opening database", err)
4849

49-
// This section will be improved so that you do not need to choose the type
50-
// of alter statements to generate. Rather, all should be generated in the
51-
// proper order.
50+
// This section will be improved so that you do not need to choose the type
51+
// of alter statements to generate. Rather, all should be generated in the
52+
// proper order.
5253
schemaType := strings.ToUpper(args[0])
5354
if schemaType == "TABLE" {
5455
compareTables(conn1, conn2)
55-
} else if schemaType == "COLUMN" {
56+
} else if schemaType == "COLUMN" {
5657
compareColumns(conn1, conn2)
57-
} else if schemaType == "FOREIGN_KEY" {
58+
} else if schemaType == "FOREIGN_KEY" {
5859
compareForeignKeys(conn1, conn2)
5960
} else {
60-
fmt.Println("Not yet handled:", schemaType)
61-
}
61+
fmt.Println("Not yet handled:", schemaType)
62+
}
6263
}
6364

6465
/*
65-
* Compare the tables in the two databases
66+
* Compare the tables in the two databases
6667
*/
6768
func compareTables(conn1 *sql.DB, conn2 *sql.DB) {
6869
sql := `
@@ -76,16 +77,16 @@ ORDER by table_name;`
7677
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
7778
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
7879

79-
// We have to explicitly type this as Schema for some reason
80+
// We have to explicitly type this as Schema for some reason
8081
var schema1 Schema = &TableSchema{channel: rowChan1}
8182
var schema2 Schema = &TableSchema{channel: rowChan2}
8283

83-
// Compare the tables
84+
// Compare the tables
8485
doDiff(schema1, schema2)
8586
}
8687

8788
/*
88-
* Compare the columns in the two databases
89+
* Compare the columns in the two databases
8990
*/
9091
func compareColumns(conn1 *sql.DB, conn2 *sql.DB) {
9192
sql := `
@@ -102,16 +103,16 @@ ORDER by table_name, column_name;`
102103
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
103104
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
104105

105-
// We have to explicitly type this as Schema for some reason
106+
// We have to explicitly type this as Schema for some reason
106107
var schema1 Schema = &ColumnSchema{channel: rowChan1}
107108
var schema2 Schema = &ColumnSchema{channel: rowChan2}
108109

109-
// Compare the columns
110+
// Compare the columns
110111
doDiff(schema1, schema2)
111112
}
112113

113114
/*
114-
* Compare the columns in the two databases
115+
* Compare the columns in the two databases
115116
*/
116117
func compareForeignKeys(conn1 *sql.DB, conn2 *sql.DB) {
117118
sql := `
@@ -132,11 +133,11 @@ ORDER BY tc.table_name, tc.constraint_name; `
132133
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
133134
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
134135

135-
// We have to explicitly type this as Schema for some reason
136+
// We have to explicitly type this as Schema for some reason
136137
var schema1 Schema = &ForeignKeySchema{channel: rowChan1}
137138
var schema2 Schema = &ForeignKeySchema{channel: rowChan2}
138139

139-
// Compare the columns
140+
// Compare the columns
140141
doDiff(schema1, schema2)
141142
}
142143

@@ -257,11 +258,11 @@ func check(msg string, err error) {
257258
}
258259

259260
func _compareString(s1 string, s2 string) int {
260-
if s1 == s2 {
261-
return 0
262-
} else if s1 < s2 {
263-
return -1
264-
} else {
265-
return +1
266-
}
261+
if s1 == s2 {
262+
return 0
263+
} else if s1 < s2 {
264+
return -1
265+
} else {
266+
return +1
267+
}
267268
}

0 commit comments

Comments
 (0)