github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/postgresql/resource_postgresql_role.go (about)

     1  package postgresql
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/errwrap"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  	"github.com/lib/pq"
    13  )
    14  
    15  const (
    16  	roleBypassRLSAttr         = "bypass_row_level_security"
    17  	roleConnLimitAttr         = "connection_limit"
    18  	roleCreateDBAttr          = "create_database"
    19  	roleCreateRoleAttr        = "create_role"
    20  	roleEncryptedPassAttr     = "encrypted_password"
    21  	roleInheritAttr           = "inherit"
    22  	roleLoginAttr             = "login"
    23  	roleNameAttr              = "name"
    24  	rolePasswordAttr          = "password"
    25  	roleReplicationAttr       = "replication"
    26  	roleSkipDropRoleAttr      = "skip_drop_role"
    27  	roleSkipReassignOwnedAttr = "skip_reassign_owned"
    28  	roleSuperuserAttr         = "superuser"
    29  	roleValidUntilAttr        = "valid_until"
    30  
    31  	// Deprecated options
    32  	roleDepEncryptedAttr = "encrypted"
    33  )
    34  
    35  func resourcePostgreSQLRole() *schema.Resource {
    36  	return &schema.Resource{
    37  		Create: resourcePostgreSQLRoleCreate,
    38  		Read:   resourcePostgreSQLRoleRead,
    39  		Update: resourcePostgreSQLRoleUpdate,
    40  		Delete: resourcePostgreSQLRoleDelete,
    41  		Exists: resourcePostgreSQLRoleExists,
    42  		Importer: &schema.ResourceImporter{
    43  			State: schema.ImportStatePassthrough,
    44  		},
    45  
    46  		Schema: map[string]*schema.Schema{
    47  			roleNameAttr: {
    48  				Type:        schema.TypeString,
    49  				Required:    true,
    50  				Description: "The name of the role",
    51  			},
    52  			rolePasswordAttr: {
    53  				Type:        schema.TypeString,
    54  				Optional:    true,
    55  				Computed:    true,
    56  				Sensitive:   true,
    57  				DefaultFunc: schema.EnvDefaultFunc("PGPASSWORD", nil),
    58  				Description: "Sets the role's password",
    59  			},
    60  			roleDepEncryptedAttr: {
    61  				Type:       schema.TypeString,
    62  				Optional:   true,
    63  				Deprecated: fmt.Sprintf("Rename PostgreSQL role resource attribute %q to %q", roleDepEncryptedAttr, roleEncryptedPassAttr),
    64  			},
    65  			roleEncryptedPassAttr: {
    66  				Type:        schema.TypeBool,
    67  				Optional:    true,
    68  				Default:     true,
    69  				Description: "Control whether the password is stored encrypted in the system catalogs",
    70  			},
    71  			roleValidUntilAttr: {
    72  				Type:        schema.TypeString,
    73  				Optional:    true,
    74  				Default:     "infinity",
    75  				Description: "Sets a date and time after which the role's password is no longer valid",
    76  			},
    77  			roleConnLimitAttr: {
    78  				Type:         schema.TypeInt,
    79  				Optional:     true,
    80  				Default:      -1,
    81  				Description:  "How many concurrent connections can be made with this role",
    82  				ValidateFunc: validateConnLimit,
    83  			},
    84  			roleSuperuserAttr: {
    85  				Type:        schema.TypeBool,
    86  				Optional:    true,
    87  				Default:     false,
    88  				Description: `Determine whether the new role is a "superuser"`,
    89  			},
    90  			roleCreateDBAttr: {
    91  				Type:        schema.TypeBool,
    92  				Optional:    true,
    93  				Default:     false,
    94  				Description: "Define a role's ability to create databases",
    95  			},
    96  			roleCreateRoleAttr: {
    97  				Type:        schema.TypeBool,
    98  				Optional:    true,
    99  				Default:     false,
   100  				Description: "Determine whether this role will be permitted to create new roles",
   101  			},
   102  			roleInheritAttr: {
   103  				Type:        schema.TypeBool,
   104  				Optional:    true,
   105  				Default:     true,
   106  				Description: `Determine whether a role "inherits" the privileges of roles it is a member of`,
   107  			},
   108  			roleLoginAttr: {
   109  				Type:        schema.TypeBool,
   110  				Optional:    true,
   111  				Default:     false,
   112  				Description: "Determine whether a role is allowed to log in",
   113  			},
   114  			roleReplicationAttr: {
   115  				Type:        schema.TypeBool,
   116  				Optional:    true,
   117  				Default:     false,
   118  				Description: "Determine whether a role is allowed to initiate streaming replication or put the system in and out of backup mode",
   119  			},
   120  			roleBypassRLSAttr: {
   121  				Type:        schema.TypeBool,
   122  				Optional:    true,
   123  				Default:     false,
   124  				Description: "Determine whether a role bypasses every row-level security (RLS) policy",
   125  			},
   126  			roleSkipDropRoleAttr: {
   127  				Type:        schema.TypeBool,
   128  				Optional:    true,
   129  				Default:     false,
   130  				Description: "Skip actually running the DROP ROLE command when removing a ROLE from PostgreSQL",
   131  			},
   132  			roleSkipReassignOwnedAttr: {
   133  				Type:        schema.TypeBool,
   134  				Optional:    true,
   135  				Default:     false,
   136  				Description: "Skip actually running the REASSIGN OWNED command when removing a role from PostgreSQL",
   137  			},
   138  		},
   139  	}
   140  }
   141  
   142  func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) error {
   143  	c := meta.(*Client)
   144  	c.catalogLock.Lock()
   145  	defer c.catalogLock.Unlock()
   146  
   147  	conn, err := c.Connect()
   148  	if err != nil {
   149  		return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err)
   150  	}
   151  	defer conn.Close()
   152  
   153  	stringOpts := []struct {
   154  		hclKey string
   155  		sqlKey string
   156  	}{
   157  		{rolePasswordAttr, "PASSWORD"},
   158  		{roleValidUntilAttr, "VALID UNTIL"},
   159  	}
   160  	intOpts := []struct {
   161  		hclKey string
   162  		sqlKey string
   163  	}{
   164  		{roleConnLimitAttr, "CONNECTION LIMIT"},
   165  	}
   166  	boolOpts := []struct {
   167  		hclKey        string
   168  		sqlKeyEnable  string
   169  		sqlKeyDisable string
   170  	}{
   171  		{roleSuperuserAttr, "CREATEDB", "NOCREATEDB"},
   172  		{roleCreateRoleAttr, "CREATEROLE", "NOCREATEROLE"},
   173  		{roleInheritAttr, "INHERIT", "NOINHERIT"},
   174  		{roleLoginAttr, "LOGIN", "NOLOGIN"},
   175  		{roleReplicationAttr, "REPLICATION", "NOREPLICATION"},
   176  		{roleBypassRLSAttr, "BYPASSRLS", "NOBYPASSRLS"},
   177  
   178  		// roleEncryptedPassAttr is used only when rolePasswordAttr is set.
   179  		// {roleEncryptedPassAttr, "ENCRYPTED", "UNENCRYPTED"},
   180  	}
   181  
   182  	createOpts := make([]string, 0, len(stringOpts)+len(intOpts)+len(boolOpts))
   183  
   184  	for _, opt := range stringOpts {
   185  		v, ok := d.GetOk(opt.hclKey)
   186  		if !ok {
   187  			continue
   188  		}
   189  
   190  		val := v.(string)
   191  		if val != "" {
   192  			switch {
   193  			case opt.hclKey == rolePasswordAttr:
   194  				if strings.ToUpper(v.(string)) == "NULL" {
   195  					createOpts = append(createOpts, "PASSWORD NULL")
   196  				} else {
   197  					if d.Get(roleEncryptedPassAttr).(bool) {
   198  						createOpts = append(createOpts, "ENCRYPTED")
   199  					} else {
   200  						createOpts = append(createOpts, "UNENCRYPTED")
   201  					}
   202  					createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, pqQuoteLiteral(val)))
   203  				}
   204  			case opt.hclKey == roleValidUntilAttr:
   205  				switch {
   206  				case v.(string) == "", strings.ToLower(v.(string)) == "infinity":
   207  					createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, "infinity"))
   208  				default:
   209  					createOpts = append(createOpts, fmt.Sprintf("%s %s", opt.sqlKey, pq.QuoteIdentifier(val)))
   210  				}
   211  			default:
   212  				createOpts = append(createOpts, fmt.Sprintf("%s %s", opt.sqlKey, pq.QuoteIdentifier(val)))
   213  			}
   214  		}
   215  	}
   216  
   217  	for _, opt := range intOpts {
   218  		val := d.Get(opt.hclKey).(int)
   219  		createOpts = append(createOpts, fmt.Sprintf("%s %d", opt.sqlKey, val))
   220  	}
   221  
   222  	for _, opt := range boolOpts {
   223  		if opt.hclKey == roleEncryptedPassAttr {
   224  			// This attribute is handled above in the stringOpts
   225  			// loop.
   226  			continue
   227  		}
   228  		val := d.Get(opt.hclKey).(bool)
   229  		valStr := opt.sqlKeyDisable
   230  		if val {
   231  			valStr = opt.sqlKeyEnable
   232  		}
   233  		createOpts = append(createOpts, valStr)
   234  	}
   235  
   236  	roleName := d.Get(roleNameAttr).(string)
   237  	createStr := strings.Join(createOpts, " ")
   238  	if len(createOpts) > 0 {
   239  		createStr = " WITH " + createStr
   240  	}
   241  
   242  	query := fmt.Sprintf("CREATE ROLE %s%s", pq.QuoteIdentifier(roleName), createStr)
   243  	_, err = conn.Query(query)
   244  	if err != nil {
   245  		return errwrap.Wrapf(fmt.Sprintf("Error creating role %s: {{err}}", roleName), err)
   246  	}
   247  
   248  	d.SetId(roleName)
   249  
   250  	return resourcePostgreSQLRoleReadImpl(d, meta)
   251  }
   252  
   253  func resourcePostgreSQLRoleDelete(d *schema.ResourceData, meta interface{}) error {
   254  	c := meta.(*Client)
   255  	c.catalogLock.Lock()
   256  	defer c.catalogLock.Unlock()
   257  
   258  	conn, err := c.Connect()
   259  	if err != nil {
   260  		return err
   261  	}
   262  	defer conn.Close()
   263  
   264  	txn, err := conn.Begin()
   265  	if err != nil {
   266  		return err
   267  	}
   268  	defer txn.Rollback()
   269  
   270  	roleName := d.Get(roleNameAttr).(string)
   271  
   272  	queries := make([]string, 0, 3)
   273  	if !d.Get(roleSkipReassignOwnedAttr).(bool) {
   274  		queries = append(queries, fmt.Sprintf("REASSIGN OWNED BY %s TO CURRENT_USER", pq.QuoteIdentifier(roleName)))
   275  		queries = append(queries, fmt.Sprintf("DROP OWNED BY %s", pq.QuoteIdentifier(roleName)))
   276  	}
   277  
   278  	if !d.Get(roleSkipDropRoleAttr).(bool) {
   279  		queries = append(queries, fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName)))
   280  	}
   281  
   282  	if len(queries) > 0 {
   283  		for _, query := range queries {
   284  			_, err = conn.Query(query)
   285  			if err != nil {
   286  				return errwrap.Wrapf("Error deleting role: {{err}}", err)
   287  			}
   288  		}
   289  
   290  		if err := txn.Commit(); err != nil {
   291  			return errwrap.Wrapf("Error committing schema: {{err}}", err)
   292  		}
   293  	}
   294  
   295  	d.SetId("")
   296  
   297  	return nil
   298  }
   299  
   300  func resourcePostgreSQLRoleExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   301  	c := meta.(*Client)
   302  	c.catalogLock.RLock()
   303  	defer c.catalogLock.RUnlock()
   304  
   305  	conn, err := c.Connect()
   306  	if err != nil {
   307  		return false, err
   308  	}
   309  	defer conn.Close()
   310  
   311  	var roleName string
   312  	err = conn.QueryRow("SELECT rolname FROM pg_catalog.pg_roles WHERE rolname=$1", d.Id()).Scan(&roleName)
   313  	switch {
   314  	case err == sql.ErrNoRows:
   315  		return false, nil
   316  	case err != nil:
   317  		return false, err
   318  	}
   319  
   320  	return true, nil
   321  }
   322  
   323  func resourcePostgreSQLRoleRead(d *schema.ResourceData, meta interface{}) error {
   324  	c := meta.(*Client)
   325  	c.catalogLock.RLock()
   326  	defer c.catalogLock.RUnlock()
   327  
   328  	return resourcePostgreSQLRoleReadImpl(d, meta)
   329  }
   330  
   331  func resourcePostgreSQLRoleReadImpl(d *schema.ResourceData, meta interface{}) error {
   332  	c := meta.(*Client)
   333  	conn, err := c.Connect()
   334  	if err != nil {
   335  		return err
   336  	}
   337  	defer conn.Close()
   338  
   339  	roleId := d.Id()
   340  	var roleSuperuser, roleInherit, roleCreateRole, roleCreateDB, roleCanLogin, roleReplication, roleBypassRLS bool
   341  	var roleConnLimit int
   342  	var roleName, roleValidUntil string
   343  	err = conn.QueryRow("SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolconnlimit, COALESCE(rolvaliduntil::TEXT, 'infinity'), rolbypassrls FROM pg_catalog.pg_roles WHERE rolname=$1", roleId).Scan(&roleName, &roleSuperuser, &roleInherit, &roleCreateRole, &roleCreateDB, &roleCanLogin, &roleReplication, &roleConnLimit, &roleValidUntil, &roleBypassRLS)
   344  	switch {
   345  	case err == sql.ErrNoRows:
   346  		log.Printf("[WARN] PostgreSQL role (%s) not found", roleId)
   347  		d.SetId("")
   348  		return nil
   349  	case err != nil:
   350  		return errwrap.Wrapf("Error reading role: {{err}}", err)
   351  	default:
   352  		d.Set(roleNameAttr, roleName)
   353  		d.Set(roleBypassRLSAttr, roleBypassRLS)
   354  		d.Set(roleConnLimitAttr, roleConnLimit)
   355  		d.Set(roleCreateDBAttr, roleCreateDB)
   356  		d.Set(roleCreateRoleAttr, roleCreateRole)
   357  		d.Set(roleEncryptedPassAttr, true)
   358  		d.Set(roleInheritAttr, roleInherit)
   359  		d.Set(roleLoginAttr, roleCanLogin)
   360  		d.Set(roleReplicationAttr, roleReplication)
   361  		d.Set(roleSkipDropRoleAttr, d.Get(roleSkipDropRoleAttr).(bool))
   362  		d.Set(roleSkipReassignOwnedAttr, d.Get(roleSkipReassignOwnedAttr).(bool))
   363  		d.Set(roleSuperuserAttr, roleSuperuser)
   364  		d.Set(roleValidUntilAttr, roleValidUntil)
   365  		d.SetId(roleName)
   366  	}
   367  
   368  	if !roleSuperuser {
   369  		// Return early if not superuser user
   370  		return nil
   371  	}
   372  
   373  	var rolePassword string
   374  	err = conn.QueryRow("SELECT COALESCE(passwd, '') FROM pg_catalog.pg_shadow AS s WHERE s.usename = $1", roleId).Scan(&rolePassword)
   375  	switch {
   376  	case err == sql.ErrNoRows:
   377  		return errwrap.Wrapf(fmt.Sprintf("PostgreSQL role (%s) not found in shadow database: {{err}}", roleId), err)
   378  	case err != nil:
   379  		return errwrap.Wrapf("Error reading role: {{err}}", err)
   380  	default:
   381  		d.Set(rolePasswordAttr, rolePassword)
   382  		return nil
   383  	}
   384  }
   385  
   386  func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) error {
   387  	c := meta.(*Client)
   388  	c.catalogLock.Lock()
   389  	defer c.catalogLock.Unlock()
   390  
   391  	conn, err := c.Connect()
   392  	if err != nil {
   393  		return err
   394  	}
   395  	defer conn.Close()
   396  
   397  	if err := setRoleName(conn, d); err != nil {
   398  		return err
   399  	}
   400  
   401  	if err := setRoleBypassRLS(conn, d); err != nil {
   402  		return err
   403  	}
   404  
   405  	if err := setRoleConnLimit(conn, d); err != nil {
   406  		return err
   407  	}
   408  
   409  	if err := setRoleCreateDB(conn, d); err != nil {
   410  		return err
   411  	}
   412  
   413  	if err := setRoleCreateRole(conn, d); err != nil {
   414  		return err
   415  	}
   416  
   417  	if err := setRoleInherit(conn, d); err != nil {
   418  		return err
   419  	}
   420  
   421  	if err := setRoleLogin(conn, d); err != nil {
   422  		return err
   423  	}
   424  
   425  	if err := setRoleReplication(conn, d); err != nil {
   426  		return err
   427  	}
   428  
   429  	if err := setRoleSuperuser(conn, d); err != nil {
   430  		return err
   431  	}
   432  
   433  	if err := setRoleValidUntil(conn, d); err != nil {
   434  		return err
   435  	}
   436  
   437  	return resourcePostgreSQLRoleReadImpl(d, meta)
   438  }
   439  
   440  func setRoleName(conn *sql.DB, d *schema.ResourceData) error {
   441  	if !d.HasChange(roleNameAttr) {
   442  		return nil
   443  	}
   444  
   445  	oraw, nraw := d.GetChange(roleNameAttr)
   446  	o := oraw.(string)
   447  	n := nraw.(string)
   448  	if n == "" {
   449  		return errors.New("Error setting role name to an empty string")
   450  	}
   451  
   452  	query := fmt.Sprintf("ALTER ROLE %s RENAME TO %s", pq.QuoteIdentifier(o), pq.QuoteIdentifier(n))
   453  	if _, err := conn.Query(query); err != nil {
   454  		return errwrap.Wrapf("Error updating role NAME: {{err}}", err)
   455  	}
   456  	d.SetId(n)
   457  
   458  	return nil
   459  }
   460  
   461  func setRoleBypassRLS(conn *sql.DB, d *schema.ResourceData) error {
   462  	if !d.HasChange(roleBypassRLSAttr) {
   463  		return nil
   464  	}
   465  
   466  	bypassRLS := d.Get(roleBypassRLSAttr).(bool)
   467  	tok := "NOBYPASSRLS"
   468  	if bypassRLS {
   469  		tok = "BYPASSRLS"
   470  	}
   471  	roleName := d.Get(roleNameAttr).(string)
   472  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   473  	if _, err := conn.Query(query); err != nil {
   474  		return errwrap.Wrapf("Error updating role BYPASSRLS: {{err}}", err)
   475  	}
   476  
   477  	return nil
   478  }
   479  
   480  func setRoleConnLimit(conn *sql.DB, d *schema.ResourceData) error {
   481  	if !d.HasChange(roleConnLimitAttr) {
   482  		return nil
   483  	}
   484  
   485  	connLimit := d.Get(roleConnLimitAttr).(int)
   486  	roleName := d.Get(roleNameAttr).(string)
   487  	query := fmt.Sprintf("ALTER ROLE %s CONNECTION LIMIT %d", pq.QuoteIdentifier(roleName), connLimit)
   488  	if _, err := conn.Query(query); err != nil {
   489  		return errwrap.Wrapf("Error updating role CONNECTION LIMIT: {{err}}", err)
   490  	}
   491  
   492  	return nil
   493  }
   494  
   495  func setRoleCreateDB(conn *sql.DB, d *schema.ResourceData) error {
   496  	if !d.HasChange(roleCreateDBAttr) {
   497  		return nil
   498  	}
   499  
   500  	createDB := d.Get(roleCreateDBAttr).(bool)
   501  	tok := "NOCREATEDB"
   502  	if createDB {
   503  		tok = "CREATEDB"
   504  	}
   505  	roleName := d.Get(roleNameAttr).(string)
   506  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   507  	if _, err := conn.Query(query); err != nil {
   508  		return errwrap.Wrapf("Error updating role CREATEDB: {{err}}", err)
   509  	}
   510  
   511  	return nil
   512  }
   513  
   514  func setRoleCreateRole(conn *sql.DB, d *schema.ResourceData) error {
   515  	if !d.HasChange(roleCreateRoleAttr) {
   516  		return nil
   517  	}
   518  
   519  	createRole := d.Get(roleCreateRoleAttr).(bool)
   520  	tok := "NOCREATEROLE"
   521  	if createRole {
   522  		tok = "CREATEROLE"
   523  	}
   524  	roleName := d.Get(roleNameAttr).(string)
   525  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   526  	if _, err := conn.Query(query); err != nil {
   527  		return errwrap.Wrapf("Error updating role CREATEROLE: {{err}}", err)
   528  	}
   529  
   530  	return nil
   531  }
   532  
   533  func setRoleInherit(conn *sql.DB, d *schema.ResourceData) error {
   534  	if !d.HasChange(roleInheritAttr) {
   535  		return nil
   536  	}
   537  
   538  	inherit := d.Get(roleInheritAttr).(bool)
   539  	tok := "NOINHERIT"
   540  	if inherit {
   541  		tok = "INHERIT"
   542  	}
   543  	roleName := d.Get(roleNameAttr).(string)
   544  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   545  	if _, err := conn.Query(query); err != nil {
   546  		return errwrap.Wrapf("Error updating role INHERIT: {{err}}", err)
   547  	}
   548  
   549  	return nil
   550  }
   551  
   552  func setRoleLogin(conn *sql.DB, d *schema.ResourceData) error {
   553  	if !d.HasChange(roleLoginAttr) {
   554  		return nil
   555  	}
   556  
   557  	login := d.Get(roleLoginAttr).(bool)
   558  	tok := "NOLOGIN"
   559  	if login {
   560  		tok = "LOGIN"
   561  	}
   562  	roleName := d.Get(roleNameAttr).(string)
   563  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   564  	if _, err := conn.Query(query); err != nil {
   565  		return errwrap.Wrapf("Error updating role LOGIN: {{err}}", err)
   566  	}
   567  
   568  	return nil
   569  }
   570  
   571  func setRoleReplication(conn *sql.DB, d *schema.ResourceData) error {
   572  	if !d.HasChange(roleReplicationAttr) {
   573  		return nil
   574  	}
   575  
   576  	replication := d.Get(roleReplicationAttr).(bool)
   577  	tok := "NOREPLICATION"
   578  	if replication {
   579  		tok = "REPLICATION"
   580  	}
   581  	roleName := d.Get(roleNameAttr).(string)
   582  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   583  	if _, err := conn.Query(query); err != nil {
   584  		return errwrap.Wrapf("Error updating role REPLICATION: {{err}}", err)
   585  	}
   586  
   587  	return nil
   588  }
   589  
   590  func setRoleSuperuser(conn *sql.DB, d *schema.ResourceData) error {
   591  	if !d.HasChange(roleSuperuserAttr) {
   592  		return nil
   593  	}
   594  
   595  	superuser := d.Get(roleSuperuserAttr).(bool)
   596  	tok := "NOSUPERUSER"
   597  	if superuser {
   598  		tok = "SUPERUSER"
   599  	}
   600  	roleName := d.Get(roleNameAttr).(string)
   601  	query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
   602  	if _, err := conn.Query(query); err != nil {
   603  		return errwrap.Wrapf("Error updating role SUPERUSER: {{err}}", err)
   604  	}
   605  
   606  	return nil
   607  }
   608  
   609  func setRoleValidUntil(conn *sql.DB, d *schema.ResourceData) error {
   610  	if !d.HasChange(roleValidUntilAttr) {
   611  		return nil
   612  	}
   613  
   614  	validUntil := d.Get(roleValidUntilAttr).(string)
   615  	if validUntil == "" {
   616  		return nil
   617  	} else if strings.ToLower(validUntil) == "infinity" {
   618  		validUntil = "infinity"
   619  	}
   620  
   621  	roleName := d.Get(roleNameAttr).(string)
   622  	query := fmt.Sprintf("ALTER ROLE %s VALID UNTIL '%s'", pq.QuoteIdentifier(roleName), pqQuoteLiteral(validUntil))
   623  
   624  	if _, err := conn.Query(query); err != nil {
   625  		return errwrap.Wrapf("Error updating role VALID UNTIL: {{err}}", err)
   626  	}
   627  
   628  	return nil
   629  }