Added BypassGet() to the topic and user caches. Moved 13 more queries to the query generator. The query generator now supports simple insert queries. Removed the pointless console logging for reply and topic deletions. We have modlogs for that. The MySQL files now have build tags to make it easy for you to disable them for other database engines. Those will be coming after the query generator is done. Moved a bunch of inline queries into more appropriate locations. Rewrote _process_where and DB_Where to better handle joins. Atom's continuing it's pointless crusade against trailing tabs.
375 lines
9.4 KiB
Go
375 lines
9.4 KiB
Go
package main
|
|
//import "fmt"
|
|
import "sync"
|
|
import "strconv"
|
|
import "html/template"
|
|
import "database/sql"
|
|
|
|
type Topic struct
|
|
{
|
|
ID int
|
|
Title string
|
|
Content string
|
|
CreatedBy int
|
|
Is_Closed bool
|
|
Sticky bool
|
|
CreatedAt string
|
|
LastReplyAt string
|
|
//LastReplyBy int
|
|
ParentID int
|
|
Status string // Deprecated. Marked for removal.
|
|
IpAddress string
|
|
PostCount int
|
|
LikeCount int
|
|
ClassName string // CSS Class Name
|
|
}
|
|
|
|
type TopicUser struct
|
|
{
|
|
ID int
|
|
Title string
|
|
Content string
|
|
CreatedBy int
|
|
Is_Closed bool
|
|
Sticky bool
|
|
CreatedAt string
|
|
LastReplyAt string
|
|
//LastReplyBy int
|
|
ParentID int
|
|
Status string // Deprecated. Marked for removal.
|
|
IpAddress string
|
|
PostCount int
|
|
LikeCount int
|
|
ClassName string
|
|
|
|
CreatedByName string
|
|
Group int
|
|
Avatar string
|
|
Css template.CSS
|
|
ContentLines int
|
|
Tag string
|
|
URL string
|
|
URLPrefix string
|
|
URLName string
|
|
Level int
|
|
Liked bool
|
|
}
|
|
|
|
type TopicsRow struct
|
|
{
|
|
ID int
|
|
Title string
|
|
Content string
|
|
CreatedBy int
|
|
Is_Closed bool
|
|
Sticky bool
|
|
CreatedAt string
|
|
LastReplyAt string
|
|
//LastReplyBy int
|
|
ParentID int
|
|
Status string // Deprecated. Marked for removal.
|
|
IpAddress string
|
|
PostCount int
|
|
LikeCount int
|
|
ClassName string
|
|
|
|
CreatedByName string
|
|
Avatar string
|
|
Css template.CSS
|
|
ContentLines int
|
|
Tag string
|
|
URL string
|
|
URLPrefix string
|
|
URLName string
|
|
Level int
|
|
|
|
ForumName string //TopicsRow
|
|
}
|
|
|
|
type TopicStore interface {
|
|
Load(id int) error
|
|
Get(id int) (*Topic, error)
|
|
GetUnsafe(id int) (*Topic, error)
|
|
CascadeGet(id int) (*Topic, error)
|
|
BypassGet(id int) (*Topic, error)
|
|
Set(item *Topic) error
|
|
Add(item *Topic) error
|
|
AddUnsafe(item *Topic) error
|
|
Remove(id int) error
|
|
RemoveUnsafe(id int) error
|
|
AddLastTopic(item *Topic, fid int) error
|
|
GetLength() int
|
|
GetCapacity() int
|
|
}
|
|
|
|
type StaticTopicStore struct {
|
|
items map[int]*Topic
|
|
length int
|
|
capacity int
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func NewStaticTopicStore(capacity int) *StaticTopicStore {
|
|
return &StaticTopicStore{items:make(map[int]*Topic),capacity:capacity}
|
|
}
|
|
|
|
func (sts *StaticTopicStore) Get(id int) (*Topic, error) {
|
|
sts.mu.RLock()
|
|
item, ok := sts.items[id]
|
|
sts.mu.RUnlock()
|
|
if ok {
|
|
return item, nil
|
|
}
|
|
return item, sql.ErrNoRows
|
|
}
|
|
|
|
func (sts *StaticTopicStore) GetUnsafe(id int) (*Topic, error) {
|
|
item, ok := sts.items[id]
|
|
if ok {
|
|
return item, nil
|
|
}
|
|
return item, sql.ErrNoRows
|
|
}
|
|
|
|
func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) {
|
|
sts.mu.RLock()
|
|
topic, ok := sts.items[id]
|
|
sts.mu.RUnlock()
|
|
if ok {
|
|
return topic, nil
|
|
}
|
|
|
|
topic = &Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
if err == nil {
|
|
sts.Add(topic)
|
|
}
|
|
return topic, err
|
|
}
|
|
|
|
func (sts *StaticTopicStore) BypassGet(id int) (*Topic, error) {
|
|
topic := &Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return topic, err
|
|
}
|
|
|
|
func (sts *StaticTopicStore) Load(id int) error {
|
|
topic := &Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
if err == nil {
|
|
sts.Set(topic)
|
|
} else {
|
|
sts.Remove(id)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (sts *StaticTopicStore) Set(item *Topic) error {
|
|
sts.mu.Lock()
|
|
_, ok := sts.items[item.ID]
|
|
if ok {
|
|
sts.items[item.ID] = item
|
|
} else if sts.length >= sts.capacity {
|
|
sts.mu.Unlock()
|
|
return ErrStoreCapacityOverflow
|
|
} else {
|
|
sts.items[item.ID] = item
|
|
sts.length++
|
|
}
|
|
sts.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (sts *StaticTopicStore) Add(item *Topic) error {
|
|
if sts.length >= sts.capacity {
|
|
return ErrStoreCapacityOverflow
|
|
}
|
|
sts.mu.Lock()
|
|
sts.items[item.ID] = item
|
|
sts.mu.Unlock()
|
|
sts.length++
|
|
return nil
|
|
}
|
|
|
|
func (sts *StaticTopicStore) AddUnsafe(item *Topic) error {
|
|
if sts.length >= sts.capacity {
|
|
return ErrStoreCapacityOverflow
|
|
}
|
|
sts.items[item.ID] = item
|
|
sts.length++
|
|
return nil
|
|
}
|
|
|
|
func (sts *StaticTopicStore) Remove(id int) error {
|
|
sts.mu.Lock()
|
|
delete(sts.items,id)
|
|
sts.mu.Unlock()
|
|
sts.length--
|
|
return nil
|
|
}
|
|
|
|
func (sts *StaticTopicStore) RemoveUnsafe(id int) error {
|
|
delete(sts.items,id)
|
|
sts.length--
|
|
return nil
|
|
}
|
|
|
|
func (sts *StaticTopicStore) AddLastTopic(item *Topic, fid int) error {
|
|
// Coming Soon...
|
|
return nil
|
|
}
|
|
|
|
func (sts *StaticTopicStore) GetLength() int {
|
|
return sts.length
|
|
}
|
|
|
|
func (sts *StaticTopicStore) SetCapacity(capacity int) {
|
|
sts.capacity = capacity
|
|
}
|
|
|
|
func (sts *StaticTopicStore) GetCapacity() int {
|
|
return sts.capacity
|
|
}
|
|
|
|
//type DynamicTopicStore struct {
|
|
// items_expiries list.List
|
|
// items map[int]*Topic
|
|
//}
|
|
|
|
type SqlTopicStore struct {
|
|
}
|
|
|
|
func NewSqlTopicStore() *SqlTopicStore {
|
|
return &SqlTopicStore{}
|
|
}
|
|
|
|
func (sus *SqlTopicStore) Get(id int) (*Topic, error) {
|
|
topic := Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return &topic, err
|
|
}
|
|
|
|
func (sus *SqlTopicStore) GetUnsafe(id int) (*Topic, error) {
|
|
topic := Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return &topic, err
|
|
}
|
|
|
|
func (sus *SqlTopicStore) CascadeGet(id int) (*Topic, error) {
|
|
topic := Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return &topic, err
|
|
}
|
|
|
|
func (sts *SqlTopicStore) BypassGet(id int) (*Topic, error) {
|
|
topic := &Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return topic, err
|
|
}
|
|
|
|
func (sus *SqlTopicStore) Load(id int) error {
|
|
topic := Topic{ID:id}
|
|
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return err
|
|
}
|
|
|
|
// Placeholder methods, the actual queries are done elsewhere
|
|
func (sus *SqlTopicStore) Set(item *Topic) error {
|
|
return nil
|
|
}
|
|
func (sus *SqlTopicStore) Add(item *Topic) error {
|
|
return nil
|
|
}
|
|
func (sus *SqlTopicStore) AddUnsafe(item *Topic) error {
|
|
return nil
|
|
}
|
|
func (sus *SqlTopicStore) Remove(id int) error {
|
|
return nil
|
|
}
|
|
func (sus *SqlTopicStore) RemoveUnsafe(id int) error {
|
|
return nil
|
|
}
|
|
func (sts *SqlTopicStore) AddLastTopic(item *Topic, fid int) error {
|
|
// Coming Soon...
|
|
return nil
|
|
}
|
|
func (sts *SqlTopicStore) GetCapacity() int {
|
|
return 0
|
|
}
|
|
|
|
func (sus *SqlTopicStore) GetLength() int {
|
|
// Return the total number of topics on the forums
|
|
return 0
|
|
}
|
|
|
|
func get_topicuser(tid int) (TopicUser,error) {
|
|
if cache_topicuser != CACHE_SQL {
|
|
topic, err := topics.Get(tid)
|
|
if err == nil {
|
|
user, err := users.CascadeGet(topic.CreatedBy)
|
|
if err != nil {
|
|
return TopicUser{ID:tid}, err
|
|
}
|
|
init_user_perms(user)
|
|
|
|
// We might be better off just passing seperate topic and user structs to the caller?
|
|
return copy_topic_to_topicuser(topic, user), nil
|
|
} else if users.GetLength() < users.GetCapacity() {
|
|
topic, err = topics.CascadeGet(tid)
|
|
if err != nil {
|
|
return TopicUser{ID:tid}, err
|
|
}
|
|
user, err := users.CascadeGet(topic.CreatedBy)
|
|
if err != nil {
|
|
return TopicUser{ID:tid}, err
|
|
}
|
|
init_user_perms(user)
|
|
tu := copy_topic_to_topicuser(topic, user)
|
|
return tu, nil
|
|
}
|
|
}
|
|
|
|
tu := TopicUser{ID:tid}
|
|
err := get_topic_user_stmt.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.Is_Closed, &tu.Sticky, &tu.ParentID, &tu.IpAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
|
|
|
|
the_topic := Topic{ID:tu.ID, Title:tu.Title, Content:tu.Content, CreatedBy:tu.CreatedBy, Is_Closed:tu.Is_Closed, Sticky:tu.Sticky, CreatedAt:tu.CreatedAt, LastReplyAt:tu.LastReplyAt, ParentID:tu.ParentID, IpAddress:tu.IpAddress, PostCount:tu.PostCount, LikeCount:tu.LikeCount}
|
|
//fmt.Printf("%+v\n", the_topic)
|
|
tu.Tag = groups[tu.Group].Tag
|
|
topics.Add(&the_topic)
|
|
//err = errors.Error("Loaded data via query")
|
|
return tu, err
|
|
}
|
|
|
|
func copy_topic_to_topicuser(topic *Topic, user *User) (tu TopicUser) {
|
|
tu.CreatedByName = user.Name
|
|
tu.Group = user.Group
|
|
tu.Avatar = user.Avatar
|
|
tu.URLPrefix = user.URLPrefix
|
|
tu.URLName = user.URLName
|
|
tu.Level = user.Level
|
|
|
|
tu.ID = topic.ID
|
|
tu.Title = topic.Title
|
|
tu.Content = topic.Content
|
|
tu.CreatedBy = topic.CreatedBy
|
|
tu.Is_Closed = topic.Is_Closed
|
|
tu.Sticky = topic.Sticky
|
|
tu.CreatedAt = topic.CreatedAt
|
|
tu.LastReplyAt = topic.LastReplyAt
|
|
tu.ParentID = topic.ParentID
|
|
tu.IpAddress = topic.IpAddress
|
|
tu.PostCount = topic.PostCount
|
|
tu.LikeCount = topic.LikeCount
|
|
return tu
|
|
}
|
|
|
|
func get_topic_by_reply(rid int) (*Topic, error) {
|
|
topic := Topic{ID:0}
|
|
err := get_topic_by_reply_stmt.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
|
|
return &topic, err
|
|
}
|
|
|
|
func build_topic_url(tid int) string {
|
|
return "/topic/" + strconv.Itoa(tid)
|
|
}
|