Skip to content

Commit b5b397f

Browse files
committed
Split grant.go into grant-relationship.go and grant-attribute.go
Former-commit-id: 7d0c6cb
1 parent 2532669 commit b5b397f

5 files changed

Lines changed: 511 additions & 346 deletions

File tree

grant-attribute.go

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package main
2+
3+
import "sort"
4+
import "fmt"
5+
import "strings"
6+
import "database/sql"
7+
import "github.com/joncrlsn/pgutil"
8+
import "github.com/joncrlsn/misc"
9+
10+
// ==================================
11+
// GrantAttributeRows definition (an array of string maps)
12+
// ==================================
13+
type GrantAttributeRows []map[string]string
14+
15+
func (slice GrantAttributeRows) Len() int {
16+
return len(slice)
17+
}
18+
19+
func (slice GrantAttributeRows) Less(i, j int) bool {
20+
if slice[i]["schema"] != slice[j]["schema"] {
21+
return slice[i]["schema"] < slice[j]["schema"]
22+
}
23+
if slice[i]["relationship_name"] != slice[j]["relationship_name"] {
24+
return slice[i]["relationship_name"] < slice[j]["relationship_name"]
25+
}
26+
if slice[i]["attribute_name"] != slice[j]["attribute_name"] {
27+
return slice[i]["attribute_name"] < slice[j]["attribute_name"]
28+
}
29+
30+
// Only compare the role part of the ACL
31+
// Not yet sure if this is absolutely necessary
32+
// (or if we could just compare the entire ACL string)
33+
role1, _ := parseAcl(slice[i]["attribute_acl"])
34+
role2, _ := parseAcl(slice[j]["attribute_acl"])
35+
if role1 != role2 {
36+
return role1 < role2
37+
}
38+
39+
return false
40+
}
41+
42+
func (slice GrantAttributeRows) Swap(i, j int) {
43+
slice[i], slice[j] = slice[j], slice[i]
44+
}
45+
46+
// ==================================
47+
// GrantAttributeSchema definition
48+
// (implements Schema -- defined in pgdiff.go)
49+
// ==================================
50+
51+
// GrantAttributeSchema holds a slice of rows from one of the databases as well as
52+
// a reference to the current row of data we're viewing.
53+
type GrantAttributeSchema struct {
54+
rows GrantAttributeRows
55+
rowNum int
56+
done bool
57+
}
58+
59+
// get returns the value from the current row for the given key
60+
func (c *GrantAttributeSchema) get(key string) string {
61+
if c.rowNum >= len(c.rows) {
62+
return ""
63+
}
64+
return c.rows[c.rowNum][key]
65+
}
66+
67+
// get returns the current row for the given key
68+
func (c *GrantAttributeSchema) getRow() map[string]string {
69+
if c.rowNum >= len(c.rows) {
70+
return make(map[string]string)
71+
}
72+
return c.rows[c.rowNum]
73+
}
74+
75+
// NextRow increments the rowNum and tells you whether or not there are more
76+
func (c *GrantAttributeSchema) NextRow() bool {
77+
if c.rowNum >= len(c.rows)-1 {
78+
c.done = true
79+
}
80+
c.rowNum = c.rowNum + 1
81+
return !c.done
82+
}
83+
84+
// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
85+
func (c *GrantAttributeSchema) Compare(obj interface{}) int {
86+
c2, ok := obj.(*GrantAttributeSchema)
87+
if !ok {
88+
fmt.Println("Error!!!, Compare needs a GrantAttributeSchema instance", c2)
89+
return +999
90+
}
91+
92+
val := misc.CompareString(c.get("schema"), c2.get("schema"))
93+
if val != 0 {
94+
return val
95+
}
96+
97+
val = misc.CompareString(c.get("relationship_name"), c2.get("relationship_name"))
98+
if val != 0 {
99+
return val
100+
}
101+
102+
val = misc.CompareString(c.get("attribute_name"), c2.get("attribute_name"))
103+
if val != 0 {
104+
return val
105+
}
106+
107+
role1, _ := parseAcl(c.get("attribute_acl"))
108+
role2, _ := parseAcl(c2.get("attribute_acl"))
109+
val = misc.CompareString(role1, role2)
110+
return val
111+
}
112+
113+
// Add prints SQL to add the column
114+
func (c *GrantAttributeSchema) Add() {
115+
fmt.Println("--Add")
116+
role, grants := parseGrants(c.get("attribute_acl"))
117+
fmt.Printf("GRANT %s (%s) ON %s TO %s;\n", strings.Join(grants, ", "), c.get("attribute_name"), c.get("relationship_name"), role)
118+
}
119+
120+
// Drop prints SQL to drop the column
121+
func (c *GrantAttributeSchema) Drop() {
122+
fmt.Println("--Drop")
123+
role, grants := parseGrants(c.get("attribute_acl"))
124+
fmt.Printf("REVOKE %s (%s) ON %s FROM %s;\n", strings.Join(grants, ", "), c.get("attribute_name"), c.get("relationship_name"), role)
125+
}
126+
127+
// Change handles the case where the relationship and column match, but the details do not
128+
func (c *GrantAttributeSchema) Change(obj interface{}) {
129+
c2, ok := obj.(*GrantAttributeSchema)
130+
if !ok {
131+
fmt.Println("-- Error!!!, Change needs a GrantAttributeSchema instance", c2)
132+
}
133+
fmt.Println("--Change")
134+
135+
role, grants1 := parseGrants(c.get("attribute_acl"))
136+
_, grants2 := parseGrants(c2.get("attribute_acl"))
137+
138+
// Find grants in the first db that are not in the second
139+
// (for this relationship and owner)
140+
grantList := make([]string, 0)
141+
for _, g := range grants1 {
142+
if !misc.ContainsString(grants2, g) {
143+
grantList = append(grantList, g)
144+
}
145+
}
146+
if len(grantList) > 0 {
147+
fmt.Printf("GRANT %s (%s) ON %s TO %s;\n", strings.Join(grantList, ", "), c.get("attribute_name"), c.get("relationship_name"), role)
148+
}
149+
150+
// Find grants in the second db that are not in the first
151+
// (for this relationship and owner)
152+
revokeList := make([]string, 0)
153+
for _, g := range grants2 {
154+
if !misc.ContainsString(grants1, g) {
155+
revokeList = append(revokeList, g)
156+
}
157+
}
158+
if len(revokeList) > 0 {
159+
fmt.Printf("REVOKE %s (%s) ON %s FROM %s;\n", strings.Join(grantList, ", "), c.get("attribute_name"), c.get("relationship_name"), role)
160+
}
161+
162+
// fmt.Printf("--1 rel:%s, relAcl:%s, col:%s, colAcl:%s\n", c.get("attribute_name"), c.get("attribute_acl"), c.get("attribute_name"), c.get("attribute_acl"))
163+
// fmt.Printf("--2 rel:%s, relAcl:%s, col:%s, colAcl:%s\n", c2.get("attribute_name"), c2.get("attribute_acl"), c2.get("attribute_name"), c2.get("attribute_acl"))
164+
}
165+
166+
// ==================================
167+
// Functions
168+
// ==================================
169+
170+
/*
171+
* Compare the columns in the two databases
172+
*/
173+
func compareGrantAttributes(conn1 *sql.DB, conn2 *sql.DB) {
174+
sql := `
175+
-- Attribute/Column ACL only
176+
SELECT
177+
n.nspname AS schema
178+
, CASE c.relkind
179+
WHEN 'r' THEN 'TABLE'
180+
WHEN 'v' THEN 'VIEW'
181+
WHEN 'f' THEN 'FOREIGN TABLE'
182+
END as type
183+
, c.relname AS relationship_name
184+
, a.attname AS attribute_name
185+
, a.attacl AS attribute_acl
186+
FROM pg_catalog.pg_class c
187+
LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
188+
INNER JOIN (SELECT attname, unnest(attacl) AS attacl, attrelid
189+
FROM pg_catalog.pg_attribute
190+
WHERE NOT attisdropped AND attacl IS NOT NULL)
191+
AS a ON (a.attrelid = c.oid)
192+
WHERE c.relkind IN ('r', 'v', 'f')
193+
AND n.nspname !~ '^pg_'
194+
AND pg_catalog.pg_table_is_visible(c.oid)
195+
ORDER BY n.nspname, c.relname, a.attname;
196+
`
197+
198+
rowChan1, _ := pgutil.QueryStrings(conn1, sql)
199+
rowChan2, _ := pgutil.QueryStrings(conn2, sql)
200+
201+
rows1 := make(GrantAttributeRows, 0)
202+
for row := range rowChan1 {
203+
rows1 = append(rows1, row)
204+
}
205+
sort.Sort(rows1)
206+
207+
rows2 := make(GrantAttributeRows, 0)
208+
for row := range rowChan2 {
209+
rows2 = append(rows2, row)
210+
}
211+
sort.Sort(rows2)
212+
213+
// We have to explicitly type this as Schema here for some unknown reason
214+
var schema1 Schema = &GrantAttributeSchema{rows: rows1, rowNum: -1}
215+
var schema2 Schema = &GrantAttributeSchema{rows: rows2, rowNum: -1}
216+
217+
doDiff(schema1, schema2)
218+
}

0 commit comments

Comments
 (0)