<commit_before>package users

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/zalando-incubator/postgres-operator/pkg/spec"
	"github.com/zalando-incubator/postgres-operator/pkg/util"
)

const (
	createUserSQL    = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;`
	alterUserSQL     = `ALTER ROLE "%s" %s`
	grantToUserSQL   = `GRANT %s TO "%s"`
	doBlockStmt      = `SET LOCAL synchronous_commit = 'local'; DO $$ BEGIN %s; END;$$;`
	passwordTemplate = "ENCRYPTED PASSWORD '%s'"
	inRoleTemplate   = `IN ROLE %s`
)

// DefaultUserSyncStrategy implements a user sync strategy that merges already existing database users
// with those defined in the manifest, altering existing users when necessary. It will never strips
// an existing roles of another role membership, nor it removes the already assigned flag
// (except for the NOLOGIN). TODO: process other NOflags, i.e. NOSUPERUSER correctly.
type DefaultUserSyncStrategy struct {
}

// ProduceSyncRequests figures out the types of changes that need to happen with the given users.
func (s DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserMap,
	newUsers spec.PgUserMap) (reqs []spec.PgSyncUserRequest) {

	// No existing roles are deleted or stripped of role memebership/flags
	for name, newUser := range newUsers {
		dbUser, exists := dbUsers[name]
		if !exists {
			reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser})
		} else {
			r := spec.PgSyncUserRequest{}
			newMD5Password := util.PGUserPassword(newUser)

			if dbUser.Password != <PASSWORD> {
				r.User.Password = <PASSWORD>
				r.Kind = spec.PGsyncUserAlter
			}
			if addNewRoles, equal := util.SubstractStringSlices(newUser.MemberOf, dbUser.MemberOf); !equal {
				r.User.MemberOf = addNewRoles
				r.Kind = spec.PGsyncUserAlter
			}
			if addNewFlags, equal := util.SubstractStringSlices(newUser.Flags, dbUser.Flags); !equal {
				r.User.Flags = addNewFlags
				r.Kind = spec.PGsyncUserAlter
			}
			if r.Kind == spec.PGsyncUserAlter {
				r.User.Name = newUser.Name
				reqs = append(reqs, r)
			}
		}
	}

	return
}

// ExecuteSyncRequests makes actual database changes from the requests passed in its arguments.
func (s DefaultUserSyncStrategy) ExecuteSyncRequests(reqs []spec.PgSyncUserRequest, db *sql.DB) error {
	for _, r := range reqs {
		switch r.Kind {
		case spec.PGSyncUserAdd:
			if err := s.createPgUser(r.User, db); err != nil {
				return fmt.Errorf("could not create user %q: %v", r.User.Name, err)
			}
		case spec.PGsyncUserAlter:
			if err := s.alterPgUser(r.User, db); err != nil {
				return fmt.Errorf("could not alter user %q: %v", r.User.Name, err)
			}
		default:
			return fmt.Errorf("unrecognized operation: %v", r.Kind)
		}

	}
	return nil
}

func (s DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) (err error) {
	var userFlags []string
	var userPassword string

	if len(user.Flags) > 0 {
		userFlags = append(userFlags, user.Flags...)
	}
	if len(user.MemberOf) > 0 {
		userFlags = append(userFlags, fmt.Sprintf(inRoleTemplate, quoteMemberList(user)))
	}

	if user.Password == "" {
		userPassword = "<PASSWORD>"
	} else {
		userPassword = fmt.Sprintf(passwordTemplate, util.PGUserPassword(user))
	}
	query := fmt.Sprintf(createUserSQL, user.Name, strings.Join(userFlags, " "), userPassword)

	_, err = db.Query(query) // TODO: Try several times
	if err != nil {
		err = fmt.Errorf("dB error: %v, query: %q", err, query)
		return
	}

	return
}

func (s DefaultUserSyncStrategy) alterPgUser(user spec.PgUser, db *sql.DB) (err error) {
	var resultStmt []string

	if user.Password != "" || len(user.Flags) > 0 {
		alterStmt := produceAlterStmt(user)
		resultStmt = append(resultStmt, alterStmt)
	}
	if len(user.MemberOf) > 0 {
		grantStmt := produceGrantStmt(user)
		resultStmt = append(resultStmt, grantStmt)
	}
	query := fmt.Sprintf(doBlockStmt, strings.Join(resultStmt, ";"))

	_, err = db.Query(query) // TODO: Try several times
	if err != nil {<commit_msg>Skip running empty set of queries<commit_after>package users

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/zalando-incubator/postgres-operator/pkg/spec"
	"github.com/zalando-incubator/postgres-operator/pkg/util"
)

const (
	createUserSQL    = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;`
	alterUserSQL     = `ALTER ROLE "%s" %s`
	grantToUserSQL   = `GRANT %s TO "%s"`
	doBlockStmt      = `SET LOCAL synchronous_commit = 'local'; DO $$ BEGIN %s; END;$$;`
	passwordTemplate = "ENCRYPTED PASSWORD '%s'"
	inRoleTemplate   = `IN ROLE %s`
)

// DefaultUserSyncStrategy implements a user sync strategy that merges already existing database users
// with those defined in the manifest, altering existing users when necessary. It will never strips
// an existing roles of another role membership, nor it removes the already assigned flag
// (except for the NOLOGIN). TODO: process other NOflags, i.e. NOSUPERUSER correctly.
type DefaultUserSyncStrategy struct {
}

// ProduceSyncRequests figures out the types of changes that need to happen with the given users.
func (s DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserMap,
	newUsers spec.PgUserMap) (reqs []spec.PgSyncUserRequest) {

	// No existing roles are deleted or stripped of role memebership/flags
	for name, newUser := range newUsers {
		dbUser, exists := dbUsers[name]
		if !exists {
			reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser})
		} else {
			r := spec.PgSyncUserRequest{}
			newMD5Password := util.PG<PASSWORD>(<PASSWORD>)

			if dbUser.Password != <PASSWORD> {
				r.User.Password = <PASSWORD>
				r.Kind = spec.PGsyncUserAlter
			}
			if addNewRoles, equal := util.SubstractStringSlices(newUser.MemberOf, dbUser.MemberOf); !equal {
				r.User.MemberOf = addNewRoles
				r.Kind = spec.PGsyncUserAlter
			}
			if addNewFlags, equal := util.SubstractStringSlices(newUser.Flags, dbUser.Flags); !equal {
				r.User.Flags = addNewFlags
				r.Kind = spec.PGsyncUserAlter
			}
			if r.Kind == spec.PGsyncUserAlter {
				r.User.Name = newUser.Name
				reqs = append(reqs, r)
			}
		}
	}

	return
}

// ExecuteSyncRequests makes actual database changes from the requests passed in its arguments.
func (s DefaultUserSyncStrategy) ExecuteSyncRequests(reqs []spec.PgSyncUserRequest, db *sql.DB) error {
	for _, r := range reqs {
		switch r.Kind {
		case spec.PGSyncUserAdd:
			if err := s.createPgUser(r.User, db); err != nil {
				return fmt.Errorf("could not create user %q: %v", r.User.Name, err)
			}
		case spec.PGsyncUserAlter:
			if err := s.alterPgUser(r.User, db); err != nil {
				return fmt.Errorf("could not alter user %q: %v", r.User.Name, err)
			}
		default:
			return fmt.Errorf("unrecognized operation: %v", r.Kind)
		}

	}
	return nil
}

func (s DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) (err error) {
	var userFlags []string
	var userPassword string

	if len(user.Flags) > 0 {
		userFlags = append(userFlags, user.Flags...)
	}
	if len(user.MemberOf) > 0 {
		userFlags = append(userFlags, fmt.Sprintf(inRoleTemplate, quoteMemberList(user)))
	}

	if user.Password == "" {
		userPassword = "<PASSWORD>"
	} else {
		userPassword = fmt.Sprintf(passwordTemplate, util.PGUserPassword(user))
	}
	query := fmt.Sprintf(createUserSQL, user.Name, strings.Join(userFlags, " "), userPassword)

	_, err = db.Query(query) // TODO: Try several times
	if err != nil {
		err = fmt.Errorf("dB error: %v, query: %q", err, query)
		return
	}

	return
}

func (s DefaultUserSyncStrategy) alterPgUser(user spec.PgUser, db *sql.DB) (err error) {
	var resultStmt []string

	if user.Password != "" || len(user.Flags) > 0 {
		alterStmt := produceAlterStmt(user)
		resultStmt = append(resultStmt, alterStmt)
	}
	if len(user.MemberOf) > 0 {
		grantStmt := produceGrantStmt(user)
		resultStmt = append(resultStmt, grantStmt)
	}
	if len(resultStmt) == 0 {
		return nil
	}

	query := fmt.Sprintf(doBlockStmt, strings.Join(resultStmt, ";"))

	_, err = db.Query(query) // TODO: Try several times
	if err != nil {
{'fraction_non_alphanumeric': 0.12327575493972909, 'fraction_numerical': 0.001366969056791351, 'mean_word_length': 4.001864512119329, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1424698', 'n_tokens_mistral': 2937, 'n_tokens_neox': 2756, 'n_words': 732}