Experiment with the accumulator.

Less boilerplate.
This commit is contained in:
Azareal 2019-10-29 11:58:04 +10:00
parent eb49dde076
commit 033f4624c8
5 changed files with 116 additions and 46 deletions

View File

@ -26,12 +26,12 @@ type Poll struct {
VoteCount int VoteCount int
} }
func (poll *Poll) CastVote(optionIndex int, uid int, ipaddress string) error { func (p *Poll) CastVote(optionIndex int, uid int, ip string) error {
return Polls.CastVote(optionIndex, poll.ID, uid, ipaddress) // TODO: Move the query into a pollStmts rather than having it in the store return Polls.CastVote(optionIndex, p.ID, uid, ip) // TODO: Move the query into a pollStmts rather than having it in the store
} }
func (poll *Poll) Copy() Poll { func (p *Poll) Copy() Poll {
return *poll return *p
} }
type PollOption struct { type PollOption struct {
@ -122,7 +122,7 @@ func (s *DefaultPollStore) Get(id int) (*Poll, error) {
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts? // TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
// TODO: ID of 0 should always error? // TODO: ID of 0 should always error?
func (s *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err error) { func (s *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err error) {
var idCount = len(ids) idCount := len(ids)
list = make(map[int]*Poll) list = make(map[int]*Poll)
if idCount == 0 { if idCount == 0 {
return list, nil return list, nil
@ -159,21 +159,21 @@ func (s *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err error)
} }
for rows.Next() { for rows.Next() {
poll := &Poll{ID: 0} p := &Poll{ID: 0}
var optionTxt []byte var optionTxt []byte
err := rows.Scan(&poll.ID, &poll.ParentID, &poll.ParentTable, &poll.Type, &optionTxt, &poll.VoteCount) err := rows.Scan(&p.ID, &p.ParentID, &p.ParentTable, &p.Type, &optionTxt, &p.VoteCount)
if err != nil { if err != nil {
return list, err return list, err
} }
err = json.Unmarshal(optionTxt, &poll.Options) err = json.Unmarshal(optionTxt, &p.Options)
if err != nil { if err != nil {
return list, err return list, err
} }
poll.QuickOptions = s.unpackOptionsMap(poll.Options) p.QuickOptions = s.unpackOptionsMap(p.Options)
s.cache.Set(poll) s.cache.Set(p)
list[poll.ID] = poll list[p.ID] = p
} }
// Did we miss any polls? // Did we miss any polls?
@ -206,22 +206,22 @@ func (s *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err error)
} }
func (s *DefaultPollStore) Reload(id int) error { func (s *DefaultPollStore) Reload(id int) error {
poll := &Poll{ID: id} p := &Poll{ID: id}
var optionTxt []byte var optionTxt []byte
err := s.get.QueryRow(id).Scan(&poll.ParentID, &poll.ParentTable, &poll.Type, &optionTxt, &poll.VoteCount) err := s.get.QueryRow(id).Scan(&p.ParentID, &p.ParentTable, &p.Type, &optionTxt, &p.VoteCount)
if err != nil { if err != nil {
s.cache.Remove(id) s.cache.Remove(id)
return err return err
} }
err = json.Unmarshal(optionTxt, &poll.Options) err = json.Unmarshal(optionTxt, &p.Options)
if err != nil { if err != nil {
s.cache.Remove(id) s.cache.Remove(id)
return err return err
} }
poll.QuickOptions = s.unpackOptionsMap(poll.Options) p.QuickOptions = s.unpackOptionsMap(p.Options)
_ = s.cache.Set(poll) _ = s.cache.Set(p)
return nil return nil
} }
@ -258,7 +258,6 @@ func (s *DefaultPollStore) Create(parent Pollable, pollType int, pollOptions map
if err != nil { if err != nil {
return 0, err return 0, err
} }
lastID, err := res.LastInsertId() lastID, err := res.LastInsertId()
if err != nil { if err != nil {
return 0, err return 0, err
@ -271,7 +270,8 @@ func (s *DefaultPollStore) Create(parent Pollable, pollType int, pollOptions map
} }
} }
return int(lastID), parent.SetPoll(int(lastID)) // TODO: Delete the poll (and options) if SetPoll fails id = int(lastID)
return id, parent.SetPoll(id) // TODO: Delete the poll (and options) if SetPoll fails
} }
func (s *DefaultPollStore) SetCache(cache PollCache) { func (s *DefaultPollStore) SetCache(cache PollCache) {

View File

@ -4,6 +4,7 @@ package qgen
import ( import (
"database/sql" "database/sql"
"log" "log"
"strings"
) )
var LogPrepares = true var LogPrepares = true
@ -235,6 +236,10 @@ func (build *Accumulator) Select(table string) *AccSelectBuilder {
return &AccSelectBuilder{table, "", "", "", "", nil, nil, "", build} return &AccSelectBuilder{table, "", "", "", "", nil, nil, "", build}
} }
func (build *Accumulator) Exists(tbl, col string) *AccSelectBuilder {
return build.Select(tbl).Columns(col).Where(col + "=?")
}
func (build *Accumulator) Insert(table string) *accInsertBuilder { func (build *Accumulator) Insert(table string) *accInsertBuilder {
return &accInsertBuilder{table, "", "", build} return &accInsertBuilder{table, "", "", build}
} }
@ -242,3 +247,68 @@ func (build *Accumulator) Insert(table string) *accInsertBuilder {
func (build *Accumulator) Count(table string) *accCountBuilder { func (build *Accumulator) Count(table string) *accCountBuilder {
return &accCountBuilder{table, "", "", nil, nil, "", build} return &accCountBuilder{table, "", "", nil, nil, "", build}
} }
type SimpleModel struct {
delete *sql.Stmt
create *sql.Stmt
update *sql.Stmt
}
func (build *Accumulator) SimpleModel(tbl, colstr, primary string) SimpleModel {
var qlist, uplist string
for _, col := range strings.Split(colstr,",") {
qlist += "?,"
uplist += col + "=?,"
}
if len(qlist) > 0 {
qlist = qlist[0 : len(qlist)-1]
uplist = uplist[0 : len(uplist)-1]
}
where := primary + "=?"
return SimpleModel{
delete: build.Delete(tbl).Where(where).Prepare(),
create: build.Insert(tbl).Columns(colstr).Fields(qlist).Prepare(),
update: build.Update(tbl).Set(uplist).Where(where).Prepare(),
}
}
func (m SimpleModel) Delete(keyVal interface{}) error {
_, err := m.delete.Exec(keyVal)
return err
}
func (m SimpleModel) Update(args ...interface{}) error {
_, err := m.update.Exec(args...)
return err
}
func (m SimpleModel) Create(args ...interface{}) error {
_, err := m.create.Exec(args...)
return err
}
func (m SimpleModel) CreateID(args ...interface{}) (int, error) {
res, err := m.create.Exec(args...)
if err != nil {
return 0, err
}
lastID, err := res.LastInsertId()
return int(lastID), err
}
func (build *Accumulator) Model(table string) *accModelBuilder {
return &accModelBuilder{table,"",build}
}
type accModelBuilder struct {
table string
primary string
build *Accumulator
}
func (b *accModelBuilder) Primary(col string) *accModelBuilder {
b.primary = col
return b
}

View File

@ -11,29 +11,29 @@ type prebuilder struct {
adapter Adapter adapter Adapter
} }
func (build *prebuilder) Select(nlist ...string) *selectPrebuilder { func (b *prebuilder) Select(nlist ...string) *selectPrebuilder {
name := optString(nlist, "") name := optString(nlist, "")
return &selectPrebuilder{name, "", "", "", "", "", nil, nil, "", build.adapter} return &selectPrebuilder{name, "", "", "", "", "", nil, nil, "", b.adapter}
} }
func (build *prebuilder) Count(nlist ...string) *selectPrebuilder { func (b *prebuilder) Count(nlist ...string) *selectPrebuilder {
name := optString(nlist, "") name := optString(nlist, "")
return &selectPrebuilder{name, "", "COUNT(*)", "", "", "", nil, nil, "", build.adapter} return &selectPrebuilder{name, "", "COUNT(*)", "", "", "", nil, nil, "", b.adapter}
} }
func (build *prebuilder) Insert(nlist ...string) *insertPrebuilder { func (b *prebuilder) Insert(nlist ...string) *insertPrebuilder {
name := optString(nlist, "") name := optString(nlist, "")
return &insertPrebuilder{name, "", "", "", build.adapter} return &insertPrebuilder{name, "", "", "", b.adapter}
} }
func (build *prebuilder) Update(nlist ...string) *updatePrebuilder { func (b *prebuilder) Update(nlist ...string) *updatePrebuilder {
name := optString(nlist, "") name := optString(nlist, "")
return &updatePrebuilder{name, "", "", "", nil, nil, build.adapter} return &updatePrebuilder{name, "", "", "", nil, nil, b.adapter}
} }
func (build *prebuilder) Delete(nlist ...string) *deletePrebuilder { func (b *prebuilder) Delete(nlist ...string) *deletePrebuilder {
name := optString(nlist, "") name := optString(nlist, "")
return &deletePrebuilder{name, "", "", nil, build.adapter} return &deletePrebuilder{name, "", "", nil, b.adapter}
} }
type deletePrebuilder struct { type deletePrebuilder struct {

View File

@ -24,49 +24,49 @@ type TransactionBuilder struct {
textToStmt map[string]*transactionStmt textToStmt map[string]*transactionStmt
} }
func (build *TransactionBuilder) SimpleDelete(table string, where string) (stmt *sql.Stmt, err error) { func (b *TransactionBuilder) SimpleDelete(table string, where string) (stmt *sql.Stmt, err error) {
res, err := build.adapter.SimpleDelete("", table, where) res, err := b.adapter.SimpleDelete("", table, where)
if err != nil { if err != nil {
return stmt, err return stmt, err
} }
return build.tx.Prepare(res) return b.tx.Prepare(res)
} }
// Quick* versions refer to it being quick to type not the performance. For performance critical transactions, you might want to use the Simple* methods or the *Tx methods on the main builder. Alternate suggestions for names are welcome :) // Quick* versions refer to it being quick to type not the performance. For performance critical transactions, you might want to use the Simple* methods or the *Tx methods on the main builder. Alternate suggestions for names are welcome :)
func (build *TransactionBuilder) QuickDelete(table string, where string) *transactionStmt { func (b *TransactionBuilder) QuickDelete(table string, where string) *transactionStmt {
res, err := build.adapter.SimpleDelete("", table, where) res, err := b.adapter.SimpleDelete("", table, where)
if err != nil { if err != nil {
return newTransactionStmt(nil, err) return newTransactionStmt(nil, err)
} }
stmt, ok := build.textToStmt[res] stmt, ok := b.textToStmt[res]
if ok { if ok {
return stmt return stmt
} }
stmt = newTransactionStmt(build.tx.Prepare(res)) stmt = newTransactionStmt(b.tx.Prepare(res))
build.textToStmt[res] = stmt b.textToStmt[res] = stmt
return stmt return stmt
} }
func (build *TransactionBuilder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) { func (b *TransactionBuilder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) {
res, err := build.adapter.SimpleInsert("", table, columns, fields) res, err := b.adapter.SimpleInsert("", table, columns, fields)
if err != nil { if err != nil {
return stmt, err return stmt, err
} }
return build.tx.Prepare(res) return b.tx.Prepare(res)
} }
func (build *TransactionBuilder) QuickInsert(table string, where string) *transactionStmt { func (b *TransactionBuilder) QuickInsert(table string, where string) *transactionStmt {
res, err := build.adapter.SimpleDelete("", table, where) res, err := b.adapter.SimpleDelete("", table, where)
if err != nil { if err != nil {
return newTransactionStmt(nil, err) return newTransactionStmt(nil, err)
} }
stmt, ok := build.textToStmt[res] stmt, ok := b.textToStmt[res]
if ok { if ok {
return stmt return stmt
} }
stmt = newTransactionStmt(build.tx.Prepare(res)) stmt = newTransactionStmt(b.tx.Prepare(res))
build.textToStmt[res] = stmt b.textToStmt[res] = stmt
return stmt return stmt
} }

View File

@ -399,7 +399,7 @@ func AddAttachToReplySubmit(w http.ResponseWriter, r *http.Request, user c.User,
elemStr = elemStr[:len(elemStr)-1] elemStr = elemStr[:len(elemStr)-1]
} }
w.Write([]byte(`{"success":"1","elems":{` + elemStr + `}}`)) w.Write([]byte(`{"success":1,"elems":{` + elemStr + `}}`))
return nil return nil
} }