@@ -3,6 +3,7 @@ package database
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
@@ -17,11 +18,24 @@ func OpenDB(conf pq.Config) {
|
||||
}
|
||||
db = sql.OpenDB(connector)
|
||||
|
||||
db.SetMaxOpenConns(500)
|
||||
db.SetMaxIdleConns(50)
|
||||
db.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Printf("Database opened to %s:%d, %s as %s!\n", conf.Host, conf.Port, conf.Database, conf.User)
|
||||
|
||||
if err := initStateDB(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := initVoteDB(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func CloseDB() {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"tomatentum.net/outfit-voting-abi26/internal"
|
||||
)
|
||||
|
||||
const INITSTATEDB string = "CREATE TABLE IF NOT EXISTS state(key varchar(255) PRIMARY KEY, value TEXT)"
|
||||
const STATEINSERT string = "INSERT INTO state(key, value) VALUES ($1, $2) ON CONFLICT (key) DO NOTHING;"
|
||||
const STATESET string = "UPDATE state SET value = $1 WHERE key = $2"
|
||||
const STATEGET string = "SELECT value FROM state WHERE key = $1"
|
||||
|
||||
const KEYVOTELOCKED string = "votinglocked"
|
||||
const KEYRESULTLOCKED string = "resultlocked"
|
||||
|
||||
var voteLocked internal.CacheItem[bool] = internal.NewCache(func() (bool, error) {
|
||||
res := db.QueryRow(STATEGET, KEYVOTELOCKED)
|
||||
|
||||
var valuestring string
|
||||
|
||||
if err := res.Scan(&valuestring); err != nil {
|
||||
return false, err
|
||||
}
|
||||
value, err := strconv.ParseBool(valuestring)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return value, nil
|
||||
}, 30)
|
||||
|
||||
var resultLocked internal.CacheItem[bool] = internal.NewCache(func() (bool, error) {
|
||||
res := db.QueryRow(STATEGET, KEYRESULTLOCKED)
|
||||
|
||||
var valuestring string
|
||||
|
||||
if err := res.Scan(&valuestring); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
value, err := strconv.ParseBool(valuestring)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return value, nil
|
||||
}, 30)
|
||||
|
||||
func initStateDB() error {
|
||||
_, err := db.Exec(INITSTATEDB)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.Exec(STATEINSERT, KEYVOTELOCKED, "true")
|
||||
_, err = db.Exec(STATEINSERT, KEYRESULTLOCKED, "true")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setState(key string, value string) error {
|
||||
_, err := db.Exec(STATESET, value, key)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetVoteLocked() (bool, error) {
|
||||
return voteLocked.Get()
|
||||
}
|
||||
|
||||
func GetResultLocked() (bool, error) {
|
||||
return resultLocked.Get()
|
||||
}
|
||||
|
||||
func SetVoteLocked(value bool) error {
|
||||
voteLocked.Invalidate()
|
||||
return setState(KEYVOTELOCKED, strconv.FormatBool(value))
|
||||
}
|
||||
|
||||
func SetResultLocked(value bool) error {
|
||||
resultLocked.Invalidate()
|
||||
return setState(KEYRESULTLOCKED, strconv.FormatBool(value))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"tomatentum.net/outfit-voting-abi26/internal/util"
|
||||
)
|
||||
|
||||
const VALUESTEMPLATE string = "(%s, %s, %s, %d)"
|
||||
|
||||
const INITVOTEDB string = "CREATE TABLE IF NOT EXISTS vote(id varchar(16) PRIMARY KEY, category TEXT NOT NULL, name TEXT NOT NULL, votes int NOT NULL)"
|
||||
const INSERTVOTEENTRY string = "INSERT INTO vote(id, category, name, votes) VALUES($1, $2, $3, $4)"
|
||||
const BULKINSERTVOTEENTRY string = "INSERT INTO vote(id, name, votes) VALUES"
|
||||
const SETVOTE string = "UPDATE vote SET votes = $1 WHERE id = $2"
|
||||
const INCVOTE string = "UPDATE vote SET votes = votes + 1 WHERE id = $1"
|
||||
const DELVOTEENTRY string = "DELETE FROM vote WHERE id = $1"
|
||||
const BULKDELVOTEENTRY string = "DELETE FROM vote WHERE key IN (%s)"
|
||||
const GETVOTES string = "SELECT * FROM vote ORDER BY votes DESC"
|
||||
const GETVOTEOPTIONS string = "SELECT id, name FROM vote"
|
||||
const GETVOTE string = "SELECT * FROM vote WHERE id = $1"
|
||||
|
||||
type VoteEntry struct {
|
||||
VoteOption
|
||||
Votes int
|
||||
}
|
||||
|
||||
type VoteOption struct {
|
||||
ID string
|
||||
Category string
|
||||
Name string
|
||||
}
|
||||
|
||||
func initVoteDB() error {
|
||||
_, err := db.Exec(INITVOTEDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func InsertVoteEntry(name string, category string) (VoteEntry, error) {
|
||||
id, err := util.RandString(8)
|
||||
|
||||
if err != nil {
|
||||
return VoteEntry{}, err
|
||||
}
|
||||
_, err = db.Exec(INSERTVOTEENTRY, id, name, 0)
|
||||
|
||||
if err != nil {
|
||||
return VoteEntry{}, err
|
||||
}
|
||||
return VoteEntry{VoteOption{id, category, name}, 0}, nil
|
||||
}
|
||||
|
||||
func BulkInsertVoteEntry(names []string, category string) (*[]VoteEntry, error) {
|
||||
stmnt := BULKINSERTVOTEENTRY
|
||||
var entries []VoteEntry = make([]VoteEntry, len(names))
|
||||
for i, v := range names {
|
||||
id, err := util.RandString(8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmnt = stmnt + fmt.Sprintf(VALUESTEMPLATE, id, category, v, 0)
|
||||
entries = append(entries, VoteEntry{VoteOption{id, category, v}, 0})
|
||||
if i < len(names)-1 {
|
||||
stmnt = stmnt + ","
|
||||
}
|
||||
}
|
||||
stmnt = stmnt + ";"
|
||||
|
||||
_, err := db.Exec(stmnt)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entries, nil
|
||||
}
|
||||
|
||||
func SetVote(id string, votes int) error {
|
||||
_, err := db.Exec(SETVOTE, votes, id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IncVote(id string) error {
|
||||
res, err := db.Exec(INCVOTE, id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, _ := res.RowsAffected()
|
||||
|
||||
if n < 1 {
|
||||
return errors.New("id does not exist")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DelVoteEntry(id string) error {
|
||||
res, err := db.Exec(DELVOTEENTRY, id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, _ := res.RowsAffected()
|
||||
|
||||
if n < 1 {
|
||||
return errors.New("id does not exist")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func BulkDelVoteEntry(ids []string) error {
|
||||
|
||||
placeholders := make([]string, len(ids))
|
||||
|
||||
for i := 0; i < len(ids); i++ {
|
||||
placeholders = append(placeholders, fmt.Sprintf("$%d", i))
|
||||
}
|
||||
|
||||
res, err := db.Exec(fmt.Sprintf(BULKDELVOTEENTRY, strings.Join(placeholders, ", ")), ids)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, _ := res.RowsAffected()
|
||||
|
||||
if n < 1 {
|
||||
return errors.New("id does not exist")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetVotes() ([]VoteEntry, error) {
|
||||
res, err := db.Query(GETVOTES)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Close()
|
||||
|
||||
var current VoteEntry
|
||||
var entries []VoteEntry = []VoteEntry{}
|
||||
|
||||
for res.Next() {
|
||||
if err := res.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := res.Scan(¤t.ID, ¤t.Category, ¤t.Name, ¤t.Votes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, current)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func GetVoteOptions() ([]VoteOption, error) {
|
||||
res, err := db.Query(GETVOTEOPTIONS)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Close()
|
||||
|
||||
var current VoteOption
|
||||
var entries []VoteOption = []VoteOption{}
|
||||
|
||||
for res.Next() {
|
||||
if err := res.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := res.Scan(¤t.ID, ¤t.Category, ¤t.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, current)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func GetVote(id string) (VoteEntry, error) {
|
||||
res := db.QueryRow(GETVOTE, id)
|
||||
|
||||
var current VoteEntry
|
||||
|
||||
if err := res.Scan(¤t.ID, ¤t.Category, ¤t.Name, ¤t.Votes); err != nil {
|
||||
return VoteEntry{}, err
|
||||
}
|
||||
|
||||
return current, nil
|
||||
}
|
||||
Reference in New Issue
Block a user