func (db *PostgresDB) persistJob(job *Job) error {
	const funcName = "persistJob"
	_, err := db.DB.Exec(insertJob, job.UUID, job.Height, job.Header)
	if err != nil {
		var pqError *pq.Error
		if errors.As(err, &pqError) {
			if pqError.Code.Name() == "unique_violation" {
				desc := fmt.Sprintf("%s: job %s already exists", funcName,
					job.UUID)
				return errs.DBError(errs.ValueFound, desc)
			}
		}
		desc := fmt.Sprintf("%s: unable to persist job: %v", funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
persistJob saves the job to the database. Returns an error if an account
already exists with the same ID.
package pool
import (
	"database/sql"
	"encoding/hex"
	"errors"
	"fmt"
	"math/big"
	"net/http"
	"time"
	"github.com/decred/dcrd/dcrutil/v3"
	"github.com/lib/pq"
	errs "github.com/decred/dcrpool/errors"
)
func InitPostgresDB(host string, port uint32, user, pass, dbName string, purgeDB bool) (*PostgresDB, error) {
	const funcName = "InitPostgresDB"
	psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
		"password=%s dbname=%s sslmode=disable",
		host, port, user, pass, dbName)
	db, err := sql.Open("postgres", psqlInfo)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to open postgres: %v", funcName, err)
		return nil, errs.DBError(errs.DBOpen, desc)
	}
	pdb := &PostgresDB{db}
	if purgeDB {
		err := pdb.purge()
		if err != nil {
			return nil, err
		}
	}
	err = db.Ping()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to connect to postgres: %v", funcName, err)
		return nil, errs.DBError(errs.DBOpen, desc)
	}
	makeErr := func(table string, err error) error {
		desc := fmt.Sprintf("%s: unable to create %s table: %v",
			funcName, table, err)
		return errs.DBError(errs.CreateStorage, desc)
	}
	_, err = db.Exec(createTableMetadata)
	if err != nil {
		return nil, makeErr("metadata", err)
	}
	_, err = db.Exec(createTableAccounts)
	if err != nil {
		return nil, makeErr("accounts", err)
	}
	_, err = db.Exec(createTablePayments)
	if err != nil {
		return nil, makeErr("payments", err)
	}
	_, err = db.Exec(createTableArchivedPayments)
	if err != nil {
		return nil, makeErr("archived payments", err)
	}
	_, err = db.Exec(createTableJobs)
	if err != nil {
		return nil, makeErr("jobs", err)
	}
	_, err = db.Exec(createTableShares)
	if err != nil {
		return nil, makeErr("shares", err)
	}
	_, err = db.Exec(createTableAcceptedWork)
	if err != nil {
		return nil, makeErr("accepted work", err)
	}
	_, err = db.Exec(createTableHashData)
	if err != nil {
		return nil, makeErr("hashrate", err)
	}
	return &PostgresDB{db}, nil
}
func (db *PostgresDB) Close() error {
	funcName := "Close"
	err := db.DB.Close()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to close db: %v", funcName, err)
		return errs.DBError(errs.DBClose, desc)
	}
	return nil
}
func (db *PostgresDB) purge() error {
	funcName := "purge"
	_, err := db.DB.Exec(purgeDB)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to purge db: %v", funcName, err)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	return nil
}
func decodePaymentRows(rows *sql.Rows) ([]*Payment, error) {
	const funcName = "decodePaymentRows"
	var toReturn []*Payment
	for rows.Next() {
		var uuid, account, transactionID, sourceBlockHash, sourceCoinbase string
		var estimatedMaturity, height, paidOnHeight uint32
		var amount, createdon int64
		err := rows.Scan(&uuid, &account, &estimatedMaturity,
			&height, &amount, &createdon, &paidOnHeight, &transactionID,
			&sourceBlockHash, &sourceCoinbase)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to scan payment entry: %v",
				funcName, err)
			return nil, errs.DBError(errs.Decode, desc)
		}
		payment := &Payment{uuid, account, estimatedMaturity,
			height, dcrutil.Amount(amount), createdon, paidOnHeight, transactionID,
			&PaymentSource{sourceBlockHash, sourceCoinbase}}
		toReturn = append(toReturn, payment)
	}
	err := rows.Err()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to decode payments: %v",
			funcName, err)
		return nil, errs.DBError(errs.Decode, desc)
	}
	return toReturn, nil
}
func decodeWorkRows(rows *sql.Rows) ([]*AcceptedWork, error) {
	const funcName = "decodeWorkRows"
	var toReturn []*AcceptedWork
	for rows.Next() {
		var uuid, blockhash, prevhash, minedby, miner string
		var confirmed bool
		var height uint32
		var createdOn int64
		err := rows.Scan(&uuid, &blockhash, &prevhash, &height,
			&minedby, &miner, &createdOn, &confirmed)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to scan work entry: %v",
				funcName, err)
			return nil, errs.DBError(errs.Decode, desc)
		}
		work := &AcceptedWork{uuid, blockhash, prevhash, height,
			minedby, miner, createdOn, confirmed}
		toReturn = append(toReturn, work)
	}
	err := rows.Err()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to decode work: %v",
			funcName, err)
		return nil, errs.DBError(errs.Decode, desc)
	}
	return toReturn, nil
}
func decodeShareRows(rows *sql.Rows) ([]*Share, error) {
	const funcName = "decodeShareRows"
	var toReturn []*Share
	for rows.Next() {
		var uuid, account, weight string
		var createdon int64
		err := rows.Scan(&uuid, &account, &weight, &createdon)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to scan share entry: %v",
				funcName, err)
			return nil, errs.DBError(errs.Decode, desc)
		}
		weightRat, ok := new(big.Rat).SetString(weight)
		if !ok {
			desc := fmt.Sprintf("unable to decode big.Rat string: %v", err)
			return nil, errs.DBError(errs.Parse, desc)
		}
		share := &Share{uuid, account, weightRat, createdon}
		toReturn = append(toReturn, share)
	}
	err := rows.Err()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to decode shares: %v",
			funcName, err)
		return nil, errs.DBError(errs.Decode, desc)
	}
	return toReturn, nil
}
func decodeHashDataRows(rows *sql.Rows) (map[string]*HashData, error) {
	const funcName = "decodeHashDataRows"
	toReturn := make(map[string]*HashData)
	for rows.Next() {
		var uuid, accountID, miner, ip, hashRate string
		var updatedOn int64
		err := rows.Scan(&uuid, &accountID, &miner, &ip,
			&hashRate, &updatedOn)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to scan hash data entry: %v",
				funcName, err)
			return nil, errs.DBError(errs.Decode, desc)
		}
		hashRat, ok := new(big.Rat).SetString(hashRate)
		if !ok {
			desc := fmt.Sprintf("%s: unable to decode big.Rat string: %v",
				funcName, err)
			return nil, errs.DBError(errs.Parse, desc)
		}
		hashData := &HashData{uuid, accountID, miner, ip, hashRat, updatedOn}
		toReturn[hashData.UUID] = hashData
	}
	err := rows.Err()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to decode hash data: %v",
			funcName, err)
		return nil, errs.DBError(errs.Decode, desc)
	}
	return toReturn, nil
}
func (db *PostgresDB) httpBackup(w http.ResponseWriter) error {
	return errs.DBError(errs.Unsupported, "httpBackup is not supported "+
		"for postgres database")
}
func (db *PostgresDB) Backup(fileName string) error {
	return errs.DBError(errs.Unsupported, "backup is not supported "+
		"for postgres database")
}
func (db *PostgresDB) fetchPoolMode() (uint32, error) {
	const funcName = "fetchPoolMode"
	var poolmode uint32
	err := db.DB.QueryRow(selectPoolMode).Scan(&poolmode)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no value found for poolmode", funcName)
			return 0, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch pool mode: %v", funcName, err)
		return 0, errs.DBError(errs.FetchEntry, desc)
	}
	return poolmode, nil
}
func (db *PostgresDB) persistPoolMode(mode uint32) error {
	const funcName = "persistPoolMode"
	_, err := db.DB.Exec(insertPoolMode, mode)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to persist pool mode: %v",
			funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) fetchCSRFSecret() ([]byte, error) {
	const funcName = "fetchCSRFSecret"
	var secret string
	err := db.DB.QueryRow(selectCSRFSecret).Scan(&secret)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no value found for csrfsecret", funcName)
			return nil, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch CSRF secret: %v",
			funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	decoded, err := hex.DecodeString(secret)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to decode csrf secret: %v",
			funcName, err)
		return nil, errs.DBError(errs.Parse, desc)
	}
	return decoded, nil
}
func (db *PostgresDB) persistCSRFSecret(secret []byte) error {
	const funcName = "persistCSRFSecret"
	_, err := db.DB.Exec(insertCSRFSecret, hex.EncodeToString(secret))
	if err != nil {
		desc := fmt.Sprintf("%s: unable to persist CSRF secrets: %v",
			funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return err
}
func (db *PostgresDB) persistLastPaymentInfo(height uint32, paidOn int64) error {
	const funcName = "persistLastPaymentInfo"
	tx, err := db.DB.Begin()
	if err != nil {
		return err
	}
	_, err = tx.Exec(insertLastPaymentHeight, height)
	if err != nil {
		rErr := tx.Rollback()
		if rErr != nil {
			desc := fmt.Sprintf("%s: unable to rollback persisting last "+
				"payment height tx: %v, initial error: %v", funcName,
				rErr, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		desc := fmt.Sprintf("%s: unable to persist last payment height: %v",
			funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	_, err = tx.Exec(insertLastPaymentPaidOn, paidOn)
	if err != nil {
		rErr := tx.Rollback()
		if rErr != nil {
			desc := fmt.Sprintf("%s: unable to rollback persist last payment "+
				"paid on time tx: %v, initial error: %v", funcName,
				rErr, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		desc := fmt.Sprintf("%s: unable to persist last payment paid on "+
			"time: %v", funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return tx.Commit()
}
func (db *PostgresDB) loadLastPaymentInfo() (uint32, int64, error) {
	const funcName = "loadLastPaymentInfo"
	var height uint32
	err := db.DB.QueryRow(selectLastPaymentHeight).Scan(&height)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no value found for lastpaymentheight",
				funcName)
			return 0, 0, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to load last payment height: %v",
			funcName, err)
		return 0, 0, errs.DBError(errs.FetchEntry, desc)
	}
	var paidOn int64
	err = db.DB.QueryRow(selectLastPaymentPaidOn).Scan(&paidOn)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no value found for lastpaymentpaidon",
				funcName)
			return 0, 0, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to load last payment paid on "+
			"time: %v", funcName, err)
		return 0, 0, errs.DBError(errs.FetchEntry, desc)
	}
	return height, paidOn, nil
}
func (db *PostgresDB) persistLastPaymentCreatedOn(createdOn int64) error {
	const funcName = "persistLastPaymentCreatedOn"
	_, err := db.DB.Exec(insertLastPaymentCreatedOn, createdOn)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to persist last payment created "+
			"on time: %v", funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) loadLastPaymentCreatedOn() (int64, error) {
	const funcName = "loadLastPaymentCreatedOn"
	var createdOn int64
	err := db.DB.QueryRow(selectLastPaymentCreatedOn).Scan(&createdOn)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no value found for lastpaymentcreatedon",
				funcName)
			return 0, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to load last payment created "+
			"on time: %v", funcName, err)
		return 0, errs.DBError(errs.PersistEntry, desc)
	}
	return createdOn, nil
}
func (db *PostgresDB) persistAccount(acc *Account) error {
	const funcName = "persistAccount"
	_, err := db.DB.Exec(insertAccount, acc.UUID, acc.Address, uint64(time.Now().Unix()))
	if err != nil {
		var pqError *pq.Error
		if errors.As(err, &pqError) {
			if pqError.Code.Name() == "unique_violation" {
				desc := fmt.Sprintf("%s: account %s already exists", funcName,
					acc.UUID)
				return errs.DBError(errs.ValueFound, desc)
			}
		}
		desc := fmt.Sprintf("%s: unable to persist account: %v", funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) fetchAccount(id string) (*Account, error) {
	const funcName = "fetchAccount"
	var uuid, address string
	var createdOn uint64
	err := db.DB.QueryRow(selectAccount, id).Scan(&uuid, &address, &createdOn)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no account found for id %s", funcName, id)
			return nil, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch account with id (%s): %v",
			funcName, id, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return &Account{uuid, address, createdOn}, nil
}
func (db *PostgresDB) deleteAccount(id string) error {
	const funcName = "deleteAccount"
	_, err := db.DB.Exec(deleteAccount, id)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to delete account with id (%s): %v",
			funcName, id, err)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	return nil
}
func (db *PostgresDB) fetchPayment(id string) (*Payment, error) {
	const funcName = "fetchPayment"
	var uuid, account, transactionID, sourceBlockHash, sourceCoinbase string
	var estimatedMaturity, height, paidOnHeight uint32
	var amount, createdOn int64
	err := db.DB.QueryRow(selectPayment, id).Scan(&uuid, &account, &estimatedMaturity,
		&height, &amount, &createdOn, &paidOnHeight, &transactionID,
		&sourceBlockHash, &sourceCoinbase)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no payment found for id %s", funcName, id)
			return nil, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch payment with id (%s): %v",
			funcName, id, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return &Payment{uuid, account, estimatedMaturity, height,
		dcrutil.Amount(amount), createdOn, paidOnHeight, transactionID,
		&PaymentSource{sourceBlockHash, sourceCoinbase}}, nil
}
func (db *PostgresDB) PersistPayment(p *Payment) error {
	const funcName = "PersistPayment"
	_, err := db.DB.Exec(insertPayment,
		p.UUID, p.Account, p.EstimatedMaturity, p.Height, p.Amount, p.CreatedOn,
		p.PaidOnHeight, p.TransactionID, p.Source.BlockHash, p.Source.Coinbase)
	if err != nil {
		var pqError *pq.Error
		if errors.As(err, &pqError) {
			if pqError.Code.Name() == "unique_violation" {
				desc := fmt.Sprintf("%s: payment %s already exists", funcName,
					p.UUID)
				return errs.DBError(errs.ValueFound, desc)
			}
		}
		desc := fmt.Sprintf("%s: unable to persist payment: %v", funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) updatePayment(p *Payment) error {
	const funcName = "updatePayment"
	_, err := db.DB.Exec(updatePayment,
		p.UUID, p.Account, p.EstimatedMaturity, p.Height, p.Amount, p.CreatedOn,
		p.PaidOnHeight, p.TransactionID, p.Source.BlockHash, p.Source.Coinbase)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to update payment with id (%s): %v",
			funcName, p.UUID, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) deletePayment(id string) error {
	const funcName = "deletePayment"
	_, err := db.DB.Exec(deletePayment, id)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to delete payment with id (%s): %v",
			funcName, id, err)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	return nil
}
func (db *PostgresDB) ArchivePayment(p *Payment) error {
	const funcName = "ArchivePayment"
	tx, err := db.DB.Begin()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to begin payment archive tx: %v",
			funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	_, err = tx.Exec(deletePayment, p.UUID)
	if err != nil {
		rErr := tx.Rollback()
		if rErr != nil {
			desc := fmt.Sprintf("%s: unable to rollback payment deletion "+
				"tx %v, initial error: %v", funcName, rErr, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		desc := fmt.Sprintf("%s: unable to delete payment: %v", funcName, rErr)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	aPmt := NewPayment(p.Account, p.Source, p.Amount, p.Height,
		p.EstimatedMaturity)
	aPmt.TransactionID = p.TransactionID
	aPmt.PaidOnHeight = p.PaidOnHeight
	_, err = tx.Exec(insertArchivedPayment,
		aPmt.UUID, aPmt.Account, aPmt.EstimatedMaturity, aPmt.Height, aPmt.Amount,
		aPmt.CreatedOn, aPmt.PaidOnHeight, aPmt.TransactionID, aPmt.Source.BlockHash,
		p.Source.Coinbase)
	if err != nil {
		rErr := tx.Rollback()
		if rErr != nil {
			desc := fmt.Sprintf("%s: unable to rollback archived payment "+
				"tx: %v, initial error: %v", funcName, rErr, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		desc := fmt.Sprintf("%s: unable to archive payment: %v", funcName, err)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	err = tx.Commit()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to commit archived payment tx: %v",
			funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) fetchPaymentsAtHeight(height uint32) ([]*Payment, error) {
	const funcName = "fetchPaymentsAtHeight"
	rows, err := db.DB.Query(selectPaymentsAtHeight, height)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch payments at height %d: %v",
			funcName, height, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodePaymentRows(rows)
}
func (db *PostgresDB) fetchPendingPayments() ([]*Payment, error) {
	const funcName = "fetchPendingPayments"
	rows, err := db.DB.Query(selectPendingPayments)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch pending payments: %v",
			funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodePaymentRows(rows)
}
func (db *PostgresDB) pendingPaymentsForBlockHash(blockHash string) (uint32, error) {
	const funcName = "pendingPaymentsForBlockHash"
	var count uint32
	err := db.DB.QueryRow(countPaymentsAtBlockHash, blockHash).Scan(&count)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch pending payments for "+
			"blockhash (%s): %v", funcName, blockHash, err)
		return 0, errs.DBError(errs.FetchEntry, desc)
	}
	return count, nil
}
func (db *PostgresDB) archivedPayments() ([]*Payment, error) {
	const funcName = "archivedPayments"
	rows, err := db.DB.Query(selectArchivedPayments)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch archived payments: %v",
			funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodePaymentRows(rows)
}
func (db *PostgresDB) maturePendingPayments(height uint32) (map[string][]*Payment, error) {
	const funcName = "maturePendingPayments"
	rows, err := db.DB.Query(selectMaturePendingPayments, height)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch mature pending payments: %v",
			funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	payments, err := decodePaymentRows(rows)
	if err != nil {
		return nil, err
	}
	pmts := make(map[string][]*Payment)
	for _, pmt := range payments {
		set, ok := pmts[pmt.Source.BlockHash]
		if !ok {
			set = make([]*Payment, 0)
		}
		set = append(set, pmt)
		pmts[pmt.Source.BlockHash] = set
	}
	return pmts, nil
}
func (db *PostgresDB) fetchShare(id string) (*Share, error) {
	const funcName = "fetchShare"
	var uuid, account, weight string
	var createdOn int64
	err := db.DB.QueryRow(selectShare, id).Scan(&uuid, &account, &weight, &createdOn)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no share found for id %s", funcName, id)
			return nil, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch share with id (%s): %v",
			funcName, id, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	weightRat, ok := new(big.Rat).SetString(weight)
	if !ok {
		desc := fmt.Sprintf("%s: unable to decode big.Rat string: %v",
			funcName, err)
		return nil, errs.DBError(errs.Parse, desc)
	}
	return &Share{uuid, account, weightRat, createdOn}, nil
}
func (db *PostgresDB) PersistShare(share *Share) error {
	const funcName = "PersistShare"
	_, err := db.DB.Exec(insertShare, share.UUID, share.Account,
		share.Weight.RatString(), share.CreatedOn)
	if err != nil {
		var pqError *pq.Error
		if errors.As(err, &pqError) {
			if pqError.Code.Name() == "unique_violation" {
				desc := fmt.Sprintf("%s: share %s already exists", funcName,
					share.UUID)
				return errs.DBError(errs.ValueFound, desc)
			}
		}
		desc := fmt.Sprintf("%s: unable to persist share: %v", funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) ppsEligibleShares(max int64) ([]*Share, error) {
	const funcName = "ppsEligibleShares"
	rows, err := db.DB.Query(selectSharesOnOrBeforeTime, max)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch PPS eligible shares: %v",
			funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodeShareRows(rows)
}
func (db *PostgresDB) pplnsEligibleShares(min int64) ([]*Share, error) {
	const funcName = "pplnsEligibleShares"
	rows, err := db.DB.Query(selectSharesAfterTime, min)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch PPLNS eligible shares: %v",
			funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodeShareRows(rows)
}
func (db *PostgresDB) pruneShares(minNano int64) error {
	const funcName = "pruneShares"
	_, err := db.DB.Exec(deleteShareCreatedBefore, minNano)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to prune shares: %v", funcName, err)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	return nil
}
func (db *PostgresDB) fetchAcceptedWork(id string) (*AcceptedWork, error) {
	const funcName = "fetchAcceptedWork"
	var uuid, blockhash, prevhash, minedby, miner string
	var confirmed bool
	var height uint32
	var createdOn int64
	err := db.DB.QueryRow(selectAcceptedWork, id).Scan(&uuid, &blockhash, &prevhash, &height,
		&minedby, &miner, &createdOn, &confirmed)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no work found for id %s", funcName, id)
			return nil, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch accepted work with id "+
			"(%s): %v", funcName, id, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return &AcceptedWork{uuid, blockhash, prevhash, height,
		minedby, miner, createdOn, confirmed}, nil
}
func (db *PostgresDB) persistAcceptedWork(work *AcceptedWork) error {
	const funcName = "persistAcceptedWork"
	_, err := db.DB.Exec(insertAcceptedWork, work.UUID, work.BlockHash, work.PrevHash,
		work.Height, work.MinedBy, work.Miner, work.CreatedOn, work.Confirmed)
	if err != nil {
		var pqError *pq.Error
		if errors.As(err, &pqError) {
			if pqError.Code.Name() == "unique_violation" {
				desc := fmt.Sprintf("%s: work %s already exists", funcName,
					work.UUID)
				return errs.DBError(errs.ValueFound, desc)
			}
		}
		desc := fmt.Sprintf("%s: unable to persist accepted work: %v",
			funcName, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	return nil
}
func (db *PostgresDB) updateAcceptedWork(work *AcceptedWork) error {
	const funcName = "updateAcceptedWork"
	result, err := db.DB.Exec(updateAcceptedWork,
		work.UUID, work.BlockHash, work.PrevHash,
		work.Height, work.MinedBy, work.Miner, work.CreatedOn, work.Confirmed)
	if err != nil {
		return err
	}
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		desc := fmt.Sprintf("%s: unable to update accepted work with id "+
			"(%s): %v", funcName, work.UUID, err)
		return errs.DBError(errs.PersistEntry, desc)
	}
	if rowsAffected == 0 {
		desc := fmt.Sprintf("%s: work %s not found", funcName, work.UUID)
		return errs.DBError(errs.ValueNotFound, desc)
	}
	return nil
}
func (db *PostgresDB) deleteAcceptedWork(id string) error {
	const funcName = "deleteAcceptedWork"
	_, err := db.DB.Exec(deleteAcceptedWork, id)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to delete account work with id "+
			"(%s): %v", funcName, id, err)
		return errs.DBError(errs.DeleteEntry, desc)
	}
	return nil
}
func (db *PostgresDB) listMinedWork() ([]*AcceptedWork, error) {
	const funcName = "listMinedWork"
	rows, err := db.DB.Query(selectMinedWork)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch mined work: %v", funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodeWorkRows(rows)
}
func (db *PostgresDB) fetchUnconfirmedWork(height uint32) ([]*AcceptedWork, error) {
	const funcName = "fetchUnconfirmedWork"
	rows, err := db.DB.Query(selectUnconfirmedWork, height)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to fetch unconfirmed work: %v", funcName, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return decodeWorkRows(rows)
}
func (db *PostgresDB) fetchJob(id string) (*Job, error) {
	const funcName = "fetchJob"
	var uuid, header string
	var height uint32
	err := db.DB.QueryRow(selectJob, id).Scan(&uuid, &header, &height)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			desc := fmt.Sprintf("%s: no job found for id %s", funcName, id)
			return nil, errs.DBError(errs.ValueNotFound, desc)
		}
		desc := fmt.Sprintf("%s: unable to fetch job with id (%s): %v",
			funcName, id, err)
		return nil, errs.DBError(errs.FetchEntry, desc)
	}
	return &Job{uuid, height, header}, nil
}