Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Postgresql 15 support #348

Merged
merged 3 commits into from
Sep 10, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
pgversion: [14, 13, 12, 11]
pgversion: [15, 14, 13, 12, 11]

env:
PGVERSION: ${{ matrix.pgversion }}
Expand Down
3 changes: 3 additions & 0 deletions postgresql/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type featureName uint

const (
featureCreateRoleWith featureName = iota
featureDatabaseOwnerRole
featureDBAllowConnections
featureDBIsTemplate
featureFallbackApplicationName
Expand Down Expand Up @@ -109,6 +110,8 @@ var (
featureFunction: semver.MustParseRange(">=8.4.0"),
// CREATE SERVER support
featureServer: semver.MustParseRange(">=10.0.0"),

featureDatabaseOwnerRole: semver.MustParseRange(">=15.0.0"),
}
)

Expand Down
33 changes: 29 additions & 4 deletions postgresql/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,15 +420,24 @@ func getDatabase(d *schema.ResourceData, databaseName string) string {
}

func getDatabaseOwner(db QueryAble, database string) (string, error) {
query := `
dbQueryString := "$1"
dbQueryValues := []interface{}{database}

// Empty means current DB
if database == "" {
dbQueryString = "current_database()"
dbQueryValues = []interface{}{}

}
query := fmt.Sprintf(`
SELECT rolname
FROM pg_database
JOIN pg_roles ON datdba = pg_roles.oid
WHERE datname = $1
`
WHERE datname = %s
`, dbQueryString)
var owner string

err := db.QueryRow(query, database).Scan(&owner)
err := db.QueryRow(query, dbQueryValues...).Scan(&owner)
switch {
case err == sql.ErrNoRows:
return "", fmt.Errorf("could not find database '%s' while looking for owner", database)
Expand Down Expand Up @@ -479,6 +488,22 @@ func getTablesOwner(db QueryAble, schemaName string) ([]string, error) {
return owners, nil
}

func resolveOwners(db QueryAble, owners []string) ([]string, error) {
resolvedOwners := []string{}
for _, owner := range owners {
if owner == "pg_database_owner" {
var err error
owner, err = getDatabaseOwner(db, "")
if err != nil {
return nil, err
}
}
resolvedOwners = append(resolvedOwners, owner)
}

return resolvedOwners, nil
}

func isSuperuser(db QueryAble, role string) (bool, error) {
var superuser bool

Expand Down
11 changes: 10 additions & 1 deletion postgresql/resource_postgresql_default_privileges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ resource postgresql_role "test_owner" {
name = "test_owner"
}

// From PostgreSQL 15, schema public is not wild open anymore
resource "postgresql_grant" "public_usage" {
database = "%s"
schema = "public"
role = postgresql_role.test_owner.name
object_type = "schema"
privileges = ["CREATE", "USAGE"]
}

resource "postgresql_default_privileges" "test_ro" {
database = "%s"
owner = postgresql_role.test_owner.name
Expand All @@ -153,7 +162,7 @@ resource "postgresql_default_privileges" "test_ro" {
object_type = "table"
privileges = ["SELECT"]
}
`, dbName, roleName)
`, dbName, dbName, roleName)

resource.Test(t, resource.TestCase{
PreCheck: func() {
Expand Down
5 changes: 5 additions & 0 deletions postgresql/resource_postgresql_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,11 @@ func getRolesToGrant(txn *sql.Tx, d *schema.ResourceData) ([]string, error) {
owners = append(owners, schemaOwner)
}

owners, err = resolveOwners(txn, owners)
if err != nil {
return nil, err
}

return owners, nil
}

Expand Down
77 changes: 77 additions & 0 deletions postgresql/resource_postgresql_grant_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package postgresql

import (
"database/sql"
"fmt"
"regexp"
"strings"
Expand Down Expand Up @@ -1287,6 +1288,15 @@ resource "postgresql_role" "test" {
login = true
}

// From PostgreSQL 15, schema public is not wild open anymore
resource "postgresql_grant" "public_usage" {
database = "postgres"
schema = "public"
role = postgresql_role.test.name
object_type = "schema"
privileges = ["CREATE", "USAGE"]
}

resource "postgresql_grant" "test" {
depends_on = [postgresql_role.test]
database = "postgres"
Expand Down Expand Up @@ -1327,6 +1337,73 @@ resource "postgresql_grant" "test" {
})
}

func TestAccPostgresqlGrantOwnerPG15(t *testing.T) {
skipIfNotAcc(t)

dbSuffix, teardown := setupTestDatabase(t, true, true)
defer teardown()

testTables := []string{"test_schema.test_table"}
createTestTables(t, dbSuffix, testTables, "")

dbName, roleName := getTestDBNames(dbSuffix)

var tfConfig = fmt.Sprintf(`
resource "postgresql_grant" "test" {
database = "%s"
role = "%s"
schema = "test_schema"
object_type = "table"
privileges = ["SELECT"]
}`, dbName, roleName)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testCheckCompatibleVersion(t, featureDatabaseOwnerRole)

// Change the owner to the new pg_database_owner role
func() {
config := getTestConfig(t)
db, err := sql.Open("postgres", config.connStr(dbName))
if err != nil {
t.Fatalf("could not connect to database %s: %v", dbName, err)
}

defer db.Close()

if _, err := db.Exec(`
ALTER SCHEMA test_schema OWNER TO pg_database_owner;
ALTER TABLE test_schema.test_table OWNER TO pg_database_owner;
`); err != nil {
t.Fatalf("could not alter owner of test_table (as %s): %v", config.Username, err)
}

}()
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: tfConfig,
Check: resource.ComposeTestCheckFunc(
func(*terraform.State) error {
return testCheckTablesPrivileges(t, dbName, roleName, []string{"test_schema.test_table"}, []string{"SELECT"})
},
),
},
{
Config: tfConfig,
Destroy: true,
Check: resource.ComposeTestCheckFunc(
func(*terraform.State) error {
return testCheckTablesPrivileges(t, dbName, roleName, []string{"test_schema.test_table"}, []string{})
},
),
},
},
})
}

func testCheckDatabasesPrivileges(t *testing.T, canCreate bool) func(*terraform.State) error {
return func(*terraform.State) error {
db := connectAsTestRole(t, "test_grant_role", "test_grant_db")
Expand Down
Loading