Skip to content

Commit 2ef4f95

Browse files
committed
Added the OWNER option to ensure tables, sequences, and views have the same owner
Former-commit-id: f88df54
1 parent ec0cb56 commit 2ef4f95

3 files changed

Lines changed: 166 additions & 13 deletions

File tree

owner.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package main
2+
3+
import "fmt"
4+
import "database/sql"
5+
import "sort"
6+
import "github.com/joncrlsn/pgutil"
7+
8+
// ==================================
9+
// OwnerRows definition (an array of string maps)
10+
// ==================================
11+
type OwnerRows []map[string]string
12+
13+
func (slice OwnerRows) Len() int {
14+
return len(slice)
15+
}
16+
17+
func (slice OwnerRows) Less(i, j int) bool {
18+
return slice[i]["relationship_name"] < slice[j]["relationship_name"]
19+
}
20+
21+
func (slice OwnerRows) Swap(i, j int) {
22+
slice[i], slice[j] = slice[j], slice[i]
23+
}
24+
25+
// ==================================
26+
// OwnerSchema definition
27+
// (implements Schema -- defined in pgdiff.go)
28+
// ==================================
29+
30+
// OwnerSchema holds a slice of rows from one of the databases as well as
31+
// a reference to the current row of data we're viewing.
32+
type OwnerSchema struct {
33+
rows OwnerRows
34+
rowNum int
35+
done bool
36+
}
37+
38+
// get returns the value from the current row for the given key
39+
func (c *OwnerSchema) get(key string) string {
40+
if c.rowNum >= len(c.rows) {
41+
return ""
42+
}
43+
return c.rows[c.rowNum][key]
44+
}
45+
46+
// get returns the current row for the given key
47+
func (c *OwnerSchema) getRow() map[string]string {
48+
if c.rowNum >= len(c.rows) {
49+
return make(map[string]string)
50+
}
51+
return c.rows[c.rowNum]
52+
}
53+
54+
// NextRow increments the rowNum and tells you whether or not there are more
55+
func (c *OwnerSchema) NextRow() bool {
56+
if c.rowNum >= len(c.rows)-1 {
57+
c.done = true
58+
}
59+
c.rowNum = c.rowNum + 1
60+
return !c.done
61+
}
62+
63+
// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
64+
func (c *OwnerSchema) Compare(obj interface{}) int {
65+
c2, ok := obj.(*OwnerSchema)
66+
if !ok {
67+
fmt.Println("Error!!!, Compare needs a OwnerSchema instance", c2)
68+
return +999
69+
}
70+
71+
val := _compareString(c.get("relationship_name"), c2.get("relationship_name"))
72+
return val
73+
}
74+
75+
// Add generates SQL to add the table/view owner
76+
func (c OwnerSchema) Add() {
77+
if c.get("type") == "TABLE" || c.get("type") == "SEQUENCE" {
78+
fmt.Printf("-- Notice, db2 has no %s. You probably need to run pgdiff with the %s option first.\n", c.get("relationship_name"), c.get("type"))
79+
} else if c.get("type") == "VIEW" {
80+
fmt.Printf("-- Notice, db2 has no view named %s. pgdiff does not yet compare view definitions.\n", c.get("relationship_name"))
81+
}
82+
}
83+
84+
// Drop generates SQL to drop the role
85+
func (c OwnerSchema) Drop() {
86+
if c.get("type") == "TABLE" || c.get("type") == "SEQUENCE" {
87+
fmt.Printf("-- Notice, db2 has a %s that db1 does not: %s. \n", c.get("type"), c.get("relationship_name"))
88+
} else if c.get("type") == "VIEW" {
89+
fmt.Printf("-- Notice, db2 has a view that db1 does not: %s. pgdiff does not yet compare view definitions.\n", c.get("relationship_name"))
90+
}
91+
}
92+
93+
// Change handles the case where the role name matches, but the details do not
94+
func (c OwnerSchema) Change(obj interface{}) {
95+
c2, ok := obj.(*OwnerSchema)
96+
if !ok {
97+
fmt.Println("-- Error!!!, Change needs a OwnerSchema instance", c2)
98+
}
99+
100+
if c.get("owner") != c2.get("owner") {
101+
fmt.Printf("ALTER %s %s OWNER TO %s; \n", c.get("type"), c.get("relationship_name"), c.get("owner"))
102+
}
103+
}
104+
105+
/*
106+
* Compare the ownership of tables, sequences, and views in the two databases
107+
*/
108+
func compareOwners(conn1 *sql.DB, conn2 *sql.DB) {
109+
sql := `
110+
SELECT n.nspname AS schema
111+
, c.relname AS relationship_name
112+
, a.rolname AS owner
113+
, CASE WHEN c.relkind = 'r' THEN 'TABLE'
114+
WHEN c.relkind = 'S' THEN 'SEQUENCE'
115+
WHEN c.relkind = 'v' THEN 'VIEW'
116+
ELSE c.relkind::varchar END AS type
117+
FROM pg_class AS c
118+
INNER JOIN pg_authid AS a ON (a.oid = c.relowner)
119+
INNER JOIN pg_namespace AS n ON (n.oid = c.relnamespace)
120+
WHERE n.nspname = 'public'
121+
AND c.relkind IN ('r', 'S', 'v');
122+
`
123+
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
124+
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
125+
126+
rows1 := make(OwnerRows, 0)
127+
for row := range rowChan1 {
128+
rows1 = append(rows1, row)
129+
}
130+
sort.Sort(rows1)
131+
132+
rows2 := make(OwnerRows, 0)
133+
for row := range rowChan2 {
134+
rows2 = append(rows2, row)
135+
}
136+
sort.Sort(rows2)
137+
138+
// We have to explicitly type this as Schema here for some unknown reason
139+
var schema1 Schema = &OwnerSchema{rows: rows1, rowNum: -1}
140+
var schema2 Schema = &OwnerSchema{rows: rows2, rowNum: -1}
141+
142+
doDiff(schema1, schema2)
143+
}

pgdiff.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func main() {
4242
// Remaining args:
4343
args = flag.Args()
4444
if len(args) == 0 {
45-
fmt.Println("The required first argument is SchemaType: SEQUENCE, TABLE, COLUMN, INDEX, FOREIGN_KEY, ROLE, GRANT")
45+
fmt.Println("The required first argument is SchemaType: ROLE, SEQUENCE, TABLE, COLUMN, INDEX, FOREIGN_KEY, GRANT")
4646
os.Exit(1)
4747
}
4848
schemaType = strings.ToUpper(args[0])
@@ -57,13 +57,16 @@ func main() {
5757
// of alter statements to generate. Rather, all should be generated in the
5858
// proper order.
5959
if schemaType == "ALL" {
60+
compareRoles(conn1, conn2)
6061
compareSequences(conn1, conn2)
6162
compareTables(conn1, conn2)
6263
compareColumns(conn1, conn2)
6364
compareIndexes(conn1, conn2) // includes PK and Unique constraints
6465
compareForeignKeys(conn1, conn2)
65-
compareRoles(conn1, conn2)
66+
compareOwners(conn1, conn2)
6667
compareGrants(conn1, conn2)
68+
} else if schemaType == "ROLE" {
69+
compareRoles(conn1, conn2)
6770
} else if schemaType == "SEQUENCE" {
6871
compareSequences(conn1, conn2)
6972
} else if schemaType == "TABLE" {
@@ -74,8 +77,8 @@ func main() {
7477
compareIndexes(conn1, conn2)
7578
} else if schemaType == "FOREIGN_KEY" {
7679
compareForeignKeys(conn1, conn2)
77-
} else if schemaType == "ROLE" {
78-
compareRoles(conn1, conn2)
80+
} else if schemaType == "OWNER" {
81+
compareOwners(conn1, conn2)
7982
} else if schemaType == "GRANT" {
8083
compareGrants(conn1, conn2)
8184
} else {

pgdiff.sh

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/bin/bash
2+
#
23
# pgdiff -U1 c42 -pw1 c422006 -d1 prd-cpc -o1 sslmode=disable -U2 c42 -pw2 c422006 -d2 cp_staging -o2 sslmode=disable COLUMN
34

45
USER1=c42
@@ -9,25 +10,31 @@ OPT1=
910
USER2=c42
1011
HOST2=fkd-msp
1112
NAME2=cp_staging
12-
OPT2="sslmode=disable"
13+
OPT2=
14+
15+
echo -n "Enter password: "; read passw
16+
PASS1=$passw
17+
PASS2=$passw
1318

1419
function rundiff() {
1520
local TYPE=$1
1621
echo "Generating diff for $TYPE..."
17-
pgdiff -U1 $USER1 -pw1 $PASS1 -h1 $HOST1 -d1 $NAME1 -o1 "$OPT1" -U2 $USER2 -pw2 $PASS2 -h2 $HOST2 -d2 $NAME2 -o2 $OPT2 $TYPE > "${TYPE}.sql"
22+
pgdiff -U1 $USER1 -pw1 $PASS1 -h1 $HOST1 -d1 $NAME1 -o1 "$OPT1" -U2 $USER2 -pw2 $PASS2 -h2 $HOST2 -d2 $NAME2 -o2 "$OPT2" $TYPE > "${TYPE}.sql"
23+
RC=$? && [[ $RC != 0 ]] && exit $RC
1824
echo -n "Press Enter to review the generated output: "; read x
1925
vi "${TYPE}.sql"
2026
echo -n "Do you wish to run this against ${NAME2}? [yN]: "; read x
2127
if [[ $x =~ y ]]; then
22-
pgrun -U $USER2 -pw $PASS2 -h $HOST2 -d $NAME2 -o $OPT2 -f "${TYPE}.sql"
28+
pgrun -U $USER2 -pw $PASS2 -h $HOST2 -d $NAME2 -o "$OPT2" -f "${TYPE}.sql"
2329
fi
2430
}
2531

26-
rundiff SEQUENCE
27-
rundiff TABLE
28-
rundiff COLUMN
29-
rundiff INDEX
30-
rundiff
31-
#rundiff FOREIGN_KEY
3232
#rundiff ROLE
33+
#rundiff SEQUENCE
34+
#rundiff TABLE
35+
#rundiff COLUMN
36+
#rundiff INDEX
37+
#rundiff FOREIGN_KEY
38+
#rundiff GRANT
39+
rundiff OWNER
3340

0 commit comments

Comments
 (0)