Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions internal/diff/default_privilege.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func generateCreateDefaultPrivilegesSQL(privileges []*ir.DefaultPrivilege, targe
context := &diffContext{
Type: DiffTypeDefaultPrivilege,
Operation: DiffOperationCreate,
Path: fmt.Sprintf("default_privileges.%s.%s", dp.ObjectType, dp.Grantee),
Path: fmt.Sprintf("default_privileges.%s.%s.%s", dp.OwnerRole, dp.ObjectType, dp.Grantee),
Source: dp,
CanRunInTransaction: true,
}
Expand All @@ -33,7 +33,7 @@ func generateDropDefaultPrivilegesSQL(privileges []*ir.DefaultPrivilege, targetS
context := &diffContext{
Type: DiffTypeDefaultPrivilege,
Operation: DiffOperationDrop,
Path: fmt.Sprintf("default_privileges.%s.%s", dp.ObjectType, dp.Grantee),
Path: fmt.Sprintf("default_privileges.%s.%s.%s", dp.OwnerRole, dp.ObjectType, dp.Grantee),
Source: dp,
CanRunInTransaction: true,
}
Expand All @@ -50,7 +50,7 @@ func generateModifyDefaultPrivilegesSQL(diffs []*defaultPrivilegeDiff, targetSch
context := &diffContext{
Type: DiffTypeDefaultPrivilege,
Operation: DiffOperationAlter,
Path: fmt.Sprintf("default_privileges.%s.%s", diff.New.ObjectType, diff.New.Grantee),
Path: fmt.Sprintf("default_privileges.%s.%s.%s", diff.New.OwnerRole, diff.New.ObjectType, diff.New.Grantee),
Source: diff,
CanRunInTransaction: true,
}
Expand All @@ -76,8 +76,8 @@ func generateGrantDefaultPrivilegeSQL(dp *ir.DefaultPrivilege, targetSchema stri
grantee = ir.QuoteIdentifier(grantee)
}

sql := fmt.Sprintf("ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT %s ON %s TO %s",
ir.QuoteIdentifier(targetSchema), privStr, dp.ObjectType, grantee)
sql := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s GRANT %s ON %s TO %s",
ir.QuoteIdentifier(dp.OwnerRole), ir.QuoteIdentifier(targetSchema), privStr, dp.ObjectType, grantee)

if dp.WithGrantOption {
sql += " WITH GRANT OPTION"
Expand All @@ -102,8 +102,8 @@ func generateRevokeDefaultPrivilegeSQL(dp *ir.DefaultPrivilege, targetSchema str
grantee = ir.QuoteIdentifier(grantee)
}

return fmt.Sprintf("ALTER DEFAULT PRIVILEGES IN SCHEMA %s REVOKE %s ON %s FROM %s;",
ir.QuoteIdentifier(targetSchema), privStr, dp.ObjectType, grantee)
return fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s REVOKE %s ON %s FROM %s;",
ir.QuoteIdentifier(dp.OwnerRole), ir.QuoteIdentifier(targetSchema), privStr, dp.ObjectType, grantee)
}

// generateAlterDefaultPrivilegeStatements generates statements for privilege modifications
Expand Down Expand Up @@ -141,20 +141,21 @@ func (d *defaultPrivilegeDiff) generateAlterDefaultPrivilegeStatements(targetSch
} else {
grantee = ir.QuoteIdentifier(grantee)
}
quotedOwner := ir.QuoteIdentifier(d.New.OwnerRole)
quotedSchema := ir.QuoteIdentifier(targetSchema)

// Generate REVOKE for removed privileges
if len(toRevoke) > 0 {
sort.Strings(toRevoke)
statements = append(statements, fmt.Sprintf("ALTER DEFAULT PRIVILEGES IN SCHEMA %s REVOKE %s ON %s FROM %s;",
quotedSchema, strings.Join(toRevoke, ", "), d.Old.ObjectType, grantee))
statements = append(statements, fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s REVOKE %s ON %s FROM %s;",
quotedOwner, quotedSchema, strings.Join(toRevoke, ", "), d.Old.ObjectType, grantee))
}

// Generate GRANT for added privileges
if len(toGrant) > 0 {
sort.Strings(toGrant)
sql := fmt.Sprintf("ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT %s ON %s TO %s",
quotedSchema, strings.Join(toGrant, ", "), d.New.ObjectType, grantee)
sql := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s GRANT %s ON %s TO %s",
quotedOwner, quotedSchema, strings.Join(toGrant, ", "), d.New.ObjectType, grantee)
if d.New.WithGrantOption {
sql += " WITH GRANT OPTION"
}
Expand All @@ -177,12 +178,12 @@ func (d *defaultPrivilegeDiff) generateAlterDefaultPrivilegeStatements(targetSch
unchangedStr := strings.Join(unchanged, ", ")

// Revoke unchanged privileges first
statements = append(statements, fmt.Sprintf("ALTER DEFAULT PRIVILEGES IN SCHEMA %s REVOKE %s ON %s FROM %s;",
quotedSchema, unchangedStr, d.New.ObjectType, grantee))
statements = append(statements, fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s REVOKE %s ON %s FROM %s;",
quotedOwner, quotedSchema, unchangedStr, d.New.ObjectType, grantee))

// Re-grant with correct option
sql := fmt.Sprintf("ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT %s ON %s TO %s",
quotedSchema, unchangedStr, d.New.ObjectType, grantee)
sql := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s GRANT %s ON %s TO %s",
quotedOwner, quotedSchema, unchangedStr, d.New.ObjectType, grantee)
if d.New.WithGrantOption {
sql += " WITH GRANT OPTION"
}
Expand All @@ -195,11 +196,14 @@ func (d *defaultPrivilegeDiff) generateAlterDefaultPrivilegeStatements(targetSch

// GetObjectName returns a unique identifier for the default privilege diff
func (d *defaultPrivilegeDiff) GetObjectName() string {
return string(d.New.ObjectType) + ":" + d.New.Grantee
return d.New.OwnerRole + ":" + string(d.New.ObjectType) + ":" + d.New.Grantee
}

// defaultPrivilegesEqual checks if two default privileges are structurally equal
func defaultPrivilegesEqual(old, new *ir.DefaultPrivilege) bool {
if old.OwnerRole != new.OwnerRole {
return false
}
if old.ObjectType != new.ObjectType {
return false
}
Expand Down
15 changes: 12 additions & 3 deletions internal/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -938,15 +938,15 @@ func GenerateMigration(oldIR, newIR *ir.IR, targetSchema string) []Diff {
// Extract default privileges from all schemas in oldIR
for _, dbSchema := range oldIR.Schemas {
for _, dp := range dbSchema.DefaultPrivileges {
key := string(dp.ObjectType) + ":" + dp.Grantee
key := dp.OwnerRole + ":" + string(dp.ObjectType) + ":" + dp.Grantee
oldDefaultPrivs[key] = dp
}
}

// Extract default privileges from all schemas in newIR
for _, dbSchema := range newIR.Schemas {
for _, dp := range dbSchema.DefaultPrivileges {
key := string(dp.ObjectType) + ":" + dp.Grantee
key := dp.OwnerRole + ":" + string(dp.ObjectType) + ":" + dp.Grantee
newDefaultPrivs[key] = dp
}
}
Expand Down Expand Up @@ -977,20 +977,29 @@ func GenerateMigration(oldIR, newIR *ir.IR, targetSchema string) []Diff {
}
}

// Sort default privileges for deterministic output
// Sort default privileges for deterministic output (by owner_role, then object_type, then grantee)
sort.Slice(diff.addedDefaultPrivileges, func(i, j int) bool {
if diff.addedDefaultPrivileges[i].OwnerRole != diff.addedDefaultPrivileges[j].OwnerRole {
return diff.addedDefaultPrivileges[i].OwnerRole < diff.addedDefaultPrivileges[j].OwnerRole
}
if diff.addedDefaultPrivileges[i].ObjectType != diff.addedDefaultPrivileges[j].ObjectType {
return diff.addedDefaultPrivileges[i].ObjectType < diff.addedDefaultPrivileges[j].ObjectType
}
return diff.addedDefaultPrivileges[i].Grantee < diff.addedDefaultPrivileges[j].Grantee
})
sort.Slice(diff.droppedDefaultPrivileges, func(i, j int) bool {
if diff.droppedDefaultPrivileges[i].OwnerRole != diff.droppedDefaultPrivileges[j].OwnerRole {
return diff.droppedDefaultPrivileges[i].OwnerRole < diff.droppedDefaultPrivileges[j].OwnerRole
}
if diff.droppedDefaultPrivileges[i].ObjectType != diff.droppedDefaultPrivileges[j].ObjectType {
return diff.droppedDefaultPrivileges[i].ObjectType < diff.droppedDefaultPrivileges[j].ObjectType
}
return diff.droppedDefaultPrivileges[i].Grantee < diff.droppedDefaultPrivileges[j].Grantee
})
sort.Slice(diff.modifiedDefaultPrivileges, func(i, j int) bool {
if diff.modifiedDefaultPrivileges[i].New.OwnerRole != diff.modifiedDefaultPrivileges[j].New.OwnerRole {
return diff.modifiedDefaultPrivileges[i].New.OwnerRole < diff.modifiedDefaultPrivileges[j].New.OwnerRole
}
if diff.modifiedDefaultPrivileges[i].New.ObjectType != diff.modifiedDefaultPrivileges[j].New.ObjectType {
return diff.modifiedDefaultPrivileges[i].New.ObjectType < diff.modifiedDefaultPrivileges[j].New.ObjectType
}
Expand Down
12 changes: 9 additions & 3 deletions ir/inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -2050,20 +2050,22 @@ func (i *Inspector) buildDefaultPrivileges(ctx context.Context, schema *IR, targ
return nil
}

// Group privileges by (object_type, grantee, is_grantable)
// Group privileges by (owner_role, object_type, grantee, is_grantable)
type privKey struct {
OwnerRole string
ObjectType string
Grantee string
WithGrantOption bool
}

grouped := make(map[privKey][]string)
for _, p := range privileges {
if !p.ObjectType.Valid || !p.Grantee.Valid || !p.PrivilegeType.Valid {
if !p.OwnerRole.Valid || !p.ObjectType.Valid || !p.Grantee.Valid || !p.PrivilegeType.Valid {
continue
}

key := privKey{
OwnerRole: p.OwnerRole.String,
ObjectType: p.ObjectType.String,
Grantee: p.Grantee.String,
WithGrantOption: p.IsGrantable.Valid && p.IsGrantable.Bool,
Expand All @@ -2078,6 +2080,7 @@ func (i *Inspector) buildDefaultPrivileges(ctx context.Context, schema *IR, targ
// Sort privileges for deterministic IR output
sort.Strings(privs)
dp := &DefaultPrivilege{
OwnerRole: key.OwnerRole,
ObjectType: DefaultPrivilegeObjectType(key.ObjectType),
Grantee: key.Grantee,
Privileges: privs,
Expand All @@ -2086,8 +2089,11 @@ func (i *Inspector) buildDefaultPrivileges(ctx context.Context, schema *IR, targ
defaultPrivileges = append(defaultPrivileges, dp)
}

// Sort for deterministic output
// Sort for deterministic output (by owner_role, then object_type, then grantee)
sort.Slice(defaultPrivileges, func(i, j int) bool {
if defaultPrivileges[i].OwnerRole != defaultPrivileges[j].OwnerRole {
return defaultPrivileges[i].OwnerRole < defaultPrivileges[j].OwnerRole
}
if defaultPrivileges[i].ObjectType != defaultPrivileges[j].ObjectType {
return defaultPrivileges[i].ObjectType < defaultPrivileges[j].ObjectType
}
Expand Down
3 changes: 2 additions & 1 deletion ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ const (

// DefaultPrivilege represents an ALTER DEFAULT PRIVILEGES setting
type DefaultPrivilege struct {
OwnerRole string `json:"owner_role"` // Role that owns the default privilege
ObjectType DefaultPrivilegeObjectType `json:"object_type"` // TABLES, SEQUENCES, FUNCTIONS, TYPES
Grantee string `json:"grantee"` // Role name or "PUBLIC"
Privileges []string `json:"privileges"` // SELECT, INSERT, UPDATE, etc.
Expand All @@ -424,7 +425,7 @@ type DefaultPrivilege struct {

// GetObjectName returns a unique identifier for the default privilege
func (d *DefaultPrivilege) GetObjectName() string {
return string(d.ObjectType) + ":" + d.Grantee
return d.OwnerRole + ":" + string(d.ObjectType) + ":" + d.Grantee
}

// PrivilegeObjectType represents the object type for explicit privilege grants
Expand Down
4 changes: 3 additions & 1 deletion ir/queries/queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,7 @@ ORDER BY n.nspname, t.typname, a.attnum;
-- name: GetDefaultPrivilegesForSchema :many
WITH acl_expanded AS (
SELECT
d.defaclrole,
d.defaclobjtype,
(aclexplode(d.defaclacl)).grantee AS grantee_oid,
(aclexplode(d.defaclacl)).privilege_type AS privilege_type,
Expand All @@ -1215,6 +1216,7 @@ WITH acl_expanded AS (
WHERE n.nspname = $1
)
SELECT
pg_get_userbyid(a.defaclrole) AS owner_role,
CASE a.defaclobjtype
WHEN 'r' THEN 'TABLES'
WHEN 'S' THEN 'SEQUENCES'
Expand All @@ -1227,7 +1229,7 @@ SELECT
a.is_grantable
FROM acl_expanded a
LEFT JOIN pg_roles r ON a.grantee_oid = r.oid
ORDER BY object_type, grantee, privilege_type;
ORDER BY owner_role, object_type, grantee, privilege_type;

-- GetPrivilegesForSchema retrieves explicit privilege grants for objects in a specific schema
-- name: GetPrivilegesForSchema :many
Expand Down
6 changes: 5 additions & 1 deletion ir/queries/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
{
"steps": [
{
"sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;",
"sql": "ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;",
"type": "default_privilege",
"operation": "create",
"path": "default_privileges.FUNCTIONS.api_user"
"path": "default_privileges.testuser.FUNCTIONS.api_user"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ Default privileges:
DDL to be executed:
--------------------------------------------------

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO api_user;
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
{
"steps": [
{
"sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;",
"sql": "ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;",
"type": "default_privilege",
"operation": "create",
"path": "default_privileges.TABLES.admin_user"
"path": "default_privileges.testuser.TABLES.admin_user"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ Default privileges:
DDL to be executed:
--------------------------------------------------

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, SELECT ON TABLES TO admin_user WITH GRANT OPTION;
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
{
"steps": [
{
"sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;",
"sql": "ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;",
"type": "default_privilege",
"operation": "create",
"path": "default_privileges.SEQUENCES.app_user"
"path": "default_privileges.testuser.SEQUENCES.app_user"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ Default privileges:
DDL to be executed:
--------------------------------------------------

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO app_user;
4 changes: 2 additions & 2 deletions testdata/diff/default_privilege/add_table_privilege/diff.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;
8 changes: 4 additions & 4 deletions testdata/diff/default_privilege/add_table_privilege/plan.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
{
"steps": [
{
"sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;",
"sql": "ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;",
"type": "default_privilege",
"operation": "create",
"path": "default_privileges.TABLES.PUBLIC"
"path": "default_privileges.testuser.TABLES.PUBLIC"
},
{
"sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;",
"sql": "ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;",
"type": "default_privilege",
"operation": "create",
"path": "default_privileges.TABLES.app_user"
"path": "default_privileges.testuser.TABLES.app_user"
}
]
}
Expand Down
4 changes: 2 additions & 2 deletions testdata/diff/default_privilege/add_table_privilege/plan.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;
4 changes: 2 additions & 2 deletions testdata/diff/default_privilege/add_table_privilege/plan.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ Default privileges:
DDL to be executed:
--------------------------------------------------

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT SELECT ON TABLES TO PUBLIC;

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE testuser IN SCHEMA public GRANT INSERT, UPDATE ON TABLES TO app_user;
Loading