diff --git a/README.md b/README.md index 51d55d02..7df471fb 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Building The Provider Clone repository to: `$GOPATH/src/github.com/cyrilgdn/terraform-provider-postgresql` ```sh -$ mkdir -p $GOPATH/src/github.com/terraform-providers; cd $GOPATH/src/github.com/terraform-providers +$ mkdir -p $GOPATH/src/github.com/cyrilgdn; cd $GOPATH/src/github.com/cyrilgdn $ git clone git@github.com:cyrilgdn/terraform-provider-postgresql ``` diff --git a/postgresql/helpers.go b/postgresql/helpers.go index bbb91b79..55c373d4 100644 --- a/postgresql/helpers.go +++ b/postgresql/helpers.go @@ -477,6 +477,19 @@ func pgLockRole(txn *sql.Tx, role string) error { return nil } +// Lock a database and all his members to avoid concurrent updates on some resources +func pgLockDatabase(txn *sql.Tx, database string) error { + // Disable statement timeout for this connection otherwise the lock could fail + if _, err := txn.Exec("SET statement_timeout = 0"); err != nil { + return fmt.Errorf("could not disable statement_timeout: %w", err) + } + if _, err := txn.Exec("SELECT pg_advisory_xact_lock(oid::bigint) FROM pg_database WHERE datname = $1", database); err != nil { + return fmt.Errorf("could not get advisory lock for database %s: %w", database, err) + } + + return nil +} + func arrayDifference(a, b []interface{}) (diff []interface{}) { m := make(map[interface{}]bool) diff --git a/postgresql/resource_postgresql_grant.go b/postgresql/resource_postgresql_grant.go index 663f64e7..952a8d66 100644 --- a/postgresql/resource_postgresql_grant.go +++ b/postgresql/resource_postgresql_grant.go @@ -150,6 +150,12 @@ func resourcePostgreSQLGrantCreate(db *DBConnection, d *schema.ResourceData) err return err } + if objectType == "database" { + if err := pgLockDatabase(txn, database); err != nil { + return err + } + } + owners, err := getRolesToGrant(txn, d) if err != nil { return err @@ -189,7 +195,8 @@ func resourcePostgreSQLGrantDelete(db *DBConnection, d *schema.ResourceData) err return fmt.Errorf("feature is not supported: %v", err) } - txn, err := startTransaction(db.client, d.Get("database").(string)) + database := d.Get("database").(string) + txn, err := startTransaction(db.client, database) if err != nil { return err } @@ -200,6 +207,13 @@ func resourcePostgreSQLGrantDelete(db *DBConnection, d *schema.ResourceData) err return err } + objectType := d.Get("object_type").(string) + if objectType == "database" { + if err := pgLockDatabase(txn, database); err != nil { + return err + } + } + owners, err := getRolesToGrant(txn, d) if err != nil { return err