@@ -11,6 +11,10 @@ import (
11
11
"github.com/lib/pq"
12
12
)
13
13
14
+ const (
15
+ roleExtensionAttrsAttr = "extension_attrs"
16
+ )
17
+
14
18
func resourcePostgreSQLRoleAttribute () * schema.Resource {
15
19
return & schema.Resource {
16
20
Create : PGResourceFunc (resourcePostgreSQLRoleAttributeCreate ),
@@ -111,6 +115,12 @@ func resourcePostgreSQLRoleAttribute() *schema.Resource {
111
115
Optional : true ,
112
116
Description : "Role to switch to at login" ,
113
117
},
118
+ roleExtensionAttrsAttr : {
119
+ Type : schema .TypeMap ,
120
+ Optional : true ,
121
+ Description : "Map of arbitrary GUC (Grand Unified Configuration) key-value pairs to set for the role. Supports all PostgreSQL custom variables." ,
122
+ Elem : & schema.Schema {Type : schema .TypeString },
123
+ },
114
124
},
115
125
}
116
126
}
@@ -271,6 +281,10 @@ func resourcePostgreSQLRoleAttributeReadImpl(db *DBConnection, d *schema.Resourc
271
281
d .Set (rolePasswordAttr , password )
272
282
}
273
283
284
+ if _ , ok := d .GetOk (roleExtensionAttrsAttr ); ok {
285
+ d .Set (roleExtensionAttrsAttr , readExtensionAttrs (roleConfig ))
286
+ }
287
+
274
288
return nil
275
289
}
276
290
@@ -401,5 +415,100 @@ func setRoleAttributes(txn *sql.Tx, db *DBConnection, d *schema.ResourceData) er
401
415
}
402
416
}
403
417
418
+ if d .HasChange (roleExtensionAttrsAttr ) {
419
+ if err := setExtensionAttrs (txn , d ); err != nil {
420
+ return err
421
+ }
422
+ }
423
+
424
+ return nil
425
+ }
426
+
427
+ // readExtensionAttrs extracts arbitrary GUC parameters from roleConfig array,
428
+ // excluding well-known parameters that are handled by specific attributes.
429
+ func readExtensionAttrs (roleConfig pq.ByteaArray ) map [string ]string {
430
+ extensionAttrs := make (map [string ]string )
431
+
432
+ // Well-known parameters that should be excluded from extension_attrs
433
+ excludedParams := map [string ]bool {
434
+ "search_path" : true ,
435
+ "statement_timeout" : true ,
436
+ "idle_in_transaction_session_timeout" : true ,
437
+ "role" : true , // assume_role
438
+ }
439
+
440
+ for _ , v := range roleConfig {
441
+ config := string (v )
442
+
443
+ // Split on first '=' to get key=value
444
+ parts := strings .SplitN (config , "=" , 2 )
445
+ if len (parts ) != 2 {
446
+ continue
447
+ }
448
+
449
+ key := parts [0 ]
450
+ value := parts [1 ]
451
+
452
+ // Skip well-known parameters
453
+ if excludedParams [key ] {
454
+ continue
455
+ }
456
+
457
+ extensionAttrs [key ] = value
458
+ }
459
+
460
+ return extensionAttrs
461
+ }
462
+
463
+ // setExtensionAttrs sets arbitrary GUC parameters for a role
464
+ func setExtensionAttrs (txn * sql.Tx , d * schema.ResourceData ) error {
465
+ roleName := d .Get (roleNameAttr ).(string )
466
+ extensionAttrsRaw := d .Get (roleExtensionAttrsAttr ).(map [string ]interface {})
467
+
468
+ // Convert interface{} map to string map
469
+ extensionAttrs := make (map [string ]string )
470
+ for k , v := range extensionAttrsRaw {
471
+ extensionAttrs [k ] = fmt .Sprintf ("%v" , v )
472
+ }
473
+
474
+ // Get old and new values to determine what needs to be changed
475
+ oldRaw , _ := d .GetChange (roleExtensionAttrsAttr )
476
+ oldAttrs := make (map [string ]string )
477
+ newAttrs := extensionAttrs
478
+
479
+ if oldRaw != nil {
480
+ oldAttrsRaw := oldRaw .(map [string ]interface {})
481
+ for k , v := range oldAttrsRaw {
482
+ oldAttrs [k ] = fmt .Sprintf ("%v" , v )
483
+ }
484
+ }
485
+
486
+ // Reset parameters that were removed
487
+ for key := range oldAttrs {
488
+ if _ , exists := newAttrs [key ]; ! exists {
489
+ sql := fmt .Sprintf ("ALTER ROLE %s RESET %s" , pq .QuoteIdentifier (roleName ), pq .QuoteIdentifier (key ))
490
+ if _ , err := txn .Exec (sql ); err != nil {
491
+ return fmt .Errorf ("could not reset %s for role %s: %w" , key , roleName , err )
492
+ }
493
+ }
494
+ }
495
+
496
+ // Set new or changed parameters
497
+ for key , value := range newAttrs {
498
+ if oldValue , exists := oldAttrs [key ]; ! exists || oldValue != value {
499
+ var sql string
500
+ if value == "" {
501
+ // Reset parameter if value is empty
502
+ sql = fmt .Sprintf ("ALTER ROLE %s RESET %s" , pq .QuoteIdentifier (roleName ), pq .QuoteIdentifier (key ))
503
+ } else {
504
+ sql = fmt .Sprintf ("ALTER ROLE %s SET %s = '%s'" , pq .QuoteIdentifier (roleName ), pq .QuoteIdentifier (key ), pqQuoteLiteral (value ))
505
+ }
506
+
507
+ if _ , err := txn .Exec (sql ); err != nil {
508
+ return fmt .Errorf ("could not set %s for role %s: %w" , key , roleName , err )
509
+ }
510
+ }
511
+ }
512
+
404
513
return nil
405
514
}
0 commit comments