Skip to content

Commit e77038c

Browse files
add idle_in_transaction_session_timeout support to role (#39)
1 parent 9926fff commit e77038c

File tree

2 files changed

+87
-17
lines changed

2 files changed

+87
-17
lines changed

postgresql/resource_postgresql_role.go

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,24 @@ import (
1616
)
1717

1818
const (
19-
roleBypassRLSAttr = "bypass_row_level_security"
20-
roleConnLimitAttr = "connection_limit"
21-
roleCreateDBAttr = "create_database"
22-
roleCreateRoleAttr = "create_role"
23-
roleEncryptedPassAttr = "encrypted_password"
24-
roleInheritAttr = "inherit"
25-
roleLoginAttr = "login"
26-
roleNameAttr = "name"
27-
rolePasswordAttr = "password"
28-
roleReplicationAttr = "replication"
29-
roleSkipDropRoleAttr = "skip_drop_role"
30-
roleSkipReassignOwnedAttr = "skip_reassign_owned"
31-
roleSuperuserAttr = "superuser"
32-
roleValidUntilAttr = "valid_until"
33-
roleRolesAttr = "roles"
34-
roleSearchPathAttr = "search_path"
35-
roleStatementTimeoutAttr = "statement_timeout"
19+
roleBypassRLSAttr = "bypass_row_level_security"
20+
roleConnLimitAttr = "connection_limit"
21+
roleCreateDBAttr = "create_database"
22+
roleCreateRoleAttr = "create_role"
23+
roleEncryptedPassAttr = "encrypted_password"
24+
roleIdleInTransactionSessionTimeoutAttr = "idle_in_transaction_session_timeout"
25+
roleInheritAttr = "inherit"
26+
roleLoginAttr = "login"
27+
roleNameAttr = "name"
28+
rolePasswordAttr = "password"
29+
roleReplicationAttr = "replication"
30+
roleSkipDropRoleAttr = "skip_drop_role"
31+
roleSkipReassignOwnedAttr = "skip_reassign_owned"
32+
roleSuperuserAttr = "superuser"
33+
roleValidUntilAttr = "valid_until"
34+
roleRolesAttr = "roles"
35+
roleSearchPathAttr = "search_path"
36+
roleStatementTimeoutAttr = "statement_timeout"
3637

3738
// Deprecated options
3839
roleDepEncryptedAttr = "encrypted"
@@ -118,6 +119,12 @@ func resourcePostgreSQLRole() *schema.Resource {
118119
Default: false,
119120
Description: "Determine whether this role will be permitted to create new roles",
120121
},
122+
roleIdleInTransactionSessionTimeoutAttr: {
123+
Type: schema.TypeInt,
124+
Optional: true,
125+
Description: "Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds",
126+
ValidateFunc: validation.IntAtLeast(0),
127+
},
121128
roleInheritAttr: {
122129
Type: schema.TypeBool,
123130
Optional: true,
@@ -290,6 +297,10 @@ func resourcePostgreSQLRoleCreate(db *DBConnection, d *schema.ResourceData) erro
290297
return err
291298
}
292299

300+
if err = setIdleInTransactionSessionTimeout(txn, d); err != nil {
301+
return err
302+
}
303+
293304
if err = txn.Commit(); err != nil {
294305
return fmt.Errorf("could not commit transaction: %w", err)
295306
}
@@ -439,6 +450,13 @@ func resourcePostgreSQLRoleReadImpl(db *DBConnection, d *schema.ResourceData) er
439450

440451
d.Set(roleStatementTimeoutAttr, statementTimeout)
441452

453+
idleInTransactionSessionTimeout, err := readIdleInTransactionSessionTimeout(roleConfig)
454+
if err != nil {
455+
return err
456+
}
457+
458+
d.Set(roleIdleInTransactionSessionTimeoutAttr, idleInTransactionSessionTimeout)
459+
442460
d.SetId(roleName)
443461

444462
password, err := readRolePassword(db, d, roleCanLogin)
@@ -466,6 +484,23 @@ func readSearchPath(roleConfig pq.ByteaArray) []string {
466484
return nil
467485
}
468486

487+
// readIdleInTransactionSessionTimeout searches for a idle_in_transaction_session_timeout entry in the rolconfig array.
488+
// In case no such value is present, it returns nil.
489+
func readIdleInTransactionSessionTimeout(roleConfig pq.ByteaArray) (int, error) {
490+
for _, v := range roleConfig {
491+
config := string(v)
492+
if strings.HasPrefix(config, roleIdleInTransactionSessionTimeoutAttr) {
493+
var result = strings.Split(strings.TrimPrefix(config, roleIdleInTransactionSessionTimeoutAttr+"="), ", ")
494+
res, err := strconv.Atoi(result[0])
495+
if err != nil {
496+
return -1, fmt.Errorf("Error reading statement_timeout: %w", err)
497+
}
498+
return res, nil
499+
}
500+
}
501+
return 0, nil
502+
}
503+
469504
// readStatementTimeout searches for a statement_timeout entry in the rolconfig array.
470505
// In case no such value is present, it returns nil.
471506
func readStatementTimeout(roleConfig pq.ByteaArray) (int, error) {
@@ -610,6 +645,10 @@ func resourcePostgreSQLRoleUpdate(db *DBConnection, d *schema.ResourceData) erro
610645
return err
611646
}
612647

648+
if err = setIdleInTransactionSessionTimeout(txn, d); err != nil {
649+
return err
650+
}
651+
613652
if err = txn.Commit(); err != nil {
614653
return fmt.Errorf("could not commit transaction: %w", err)
615654
}
@@ -933,3 +972,28 @@ func setStatementTimeout(txn *sql.Tx, d *schema.ResourceData) error {
933972
}
934973
return nil
935974
}
975+
976+
func setIdleInTransactionSessionTimeout(txn *sql.Tx, d *schema.ResourceData) error {
977+
if !d.HasChange(roleIdleInTransactionSessionTimeoutAttr) {
978+
return nil
979+
}
980+
981+
roleName := d.Get(roleNameAttr).(string)
982+
idleInTransactionSessionTimeout := d.Get(roleIdleInTransactionSessionTimeoutAttr).(int)
983+
if idleInTransactionSessionTimeout != 0 {
984+
sql := fmt.Sprintf(
985+
"ALTER ROLE %s SET idle_in_transaction_session_timeout TO %d", pq.QuoteIdentifier(roleName), idleInTransactionSessionTimeout,
986+
)
987+
if _, err := txn.Exec(sql); err != nil {
988+
return fmt.Errorf("could not set idle_in_transaction_session_timeout %d for %s: %w", idleInTransactionSessionTimeout, roleName, err)
989+
}
990+
} else {
991+
sql := fmt.Sprintf(
992+
"ALTER ROLE %s RESET idle_in_transaction_session_timeout", pq.QuoteIdentifier(roleName),
993+
)
994+
if _, err := txn.Exec(sql); err != nil {
995+
return fmt.Errorf("could not reset idle_in_transaction_session_timeout for %s: %w", roleName, err)
996+
}
997+
}
998+
return nil
999+
}

postgresql/resource_postgresql_role_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func TestAccPostgresqlRole_Basic(t *testing.T) {
4545
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "skip_drop_role", "false"),
4646
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "skip_reassign_owned", "false"),
4747
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "statement_timeout", "0"),
48+
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "idle_in_transaction_session_timeout", "0"),
4849

4950
resource.TestCheckResourceAttr("postgresql_role.role_with_create_database", "name", "role_with_create_database"),
5051
resource.TestCheckResourceAttr("postgresql_role.role_with_create_database", "create_database", "true"),
@@ -120,6 +121,7 @@ resource "postgresql_role" "update_role" {
120121
roles = ["${postgresql_role.group_role.name}"]
121122
search_path = ["mysearchpath"]
122123
statement_timeout = 30000
124+
idle_in_transaction_session_timeout = 60000
123125
}
124126
`
125127
resource.Test(t, resource.TestCase{
@@ -142,6 +144,7 @@ resource "postgresql_role" "update_role" {
142144
resource.TestCheckResourceAttr("postgresql_role.update_role", "roles.#", "0"),
143145
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"),
144146
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"),
147+
resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"),
145148
testAccCheckRoleCanLogin(t, "update_role", "toto"),
146149
),
147150
},
@@ -164,6 +167,7 @@ resource "postgresql_role" "update_role" {
164167
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "1"),
165168
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.0", "mysearchpath"),
166169
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "30000"),
170+
resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "60000"),
167171
testAccCheckRoleCanLogin(t, "update_role2", "titi"),
168172
),
169173
},
@@ -180,6 +184,7 @@ resource "postgresql_role" "update_role" {
180184
resource.TestCheckResourceAttr("postgresql_role.update_role", "roles.#", "0"),
181185
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"),
182186
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"),
187+
resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"),
183188
testAccCheckRoleCanLogin(t, "update_role", "toto"),
184189
),
185190
},
@@ -411,6 +416,7 @@ resource "postgresql_role" "role_with_defaults" {
411416
skip_drop_role = false
412417
valid_until = "infinity"
413418
statement_timeout = 0
419+
idle_in_transaction_session_timeout = 0
414420
}
415421
416422
resource "postgresql_role" "role_with_create_database" {

0 commit comments

Comments
 (0)