diff --git a/database.go b/database.go index 9d454840..b372bf3e 100644 --- a/database.go +++ b/database.go @@ -3,6 +3,11 @@ package main import "log" import "fmt" import "encoding/json" +import "database/sql" + +var db *sql.DB +var db_version string +var db_collation string = "utf8mb4_general_ci" func init_database() (err error) { // Engine specific code @@ -66,6 +71,7 @@ func init_database() (err error) { if err != nil { return err } + fstore = NewStaticForumStore() log.Print("Loading the settings.") err = LoadSettings() diff --git a/forum.go b/forum.go index 49315502..9d9957d8 100644 --- a/forum.go +++ b/forum.go @@ -41,16 +41,6 @@ type ForumSimple struct Preset string } -/*type ForumStore interface -{ - Get(int) (*Forum, error) - CascadeGet(int) (*Forum, error) - Update(Forum) error - CascadeUpdate(Forum) error - Delete(int) error - CascadeDelete(int) error -}*/ - func LoadForums() error { //if debug { log.Print("Adding the uncategorised forum") @@ -95,6 +85,7 @@ func LoadForums() error { } var forum_update_mutex sync.Mutex +var forum_create_mutex sync.Mutex func create_forum(forum_name string, forum_desc string, active bool, preset string) (int, error) { var fid int err := forum_entry_exists_stmt.QueryRow().Scan(&fid) @@ -115,6 +106,7 @@ func create_forum(forum_name string, forum_desc string, active bool, preset stri return fid, nil } + forum_create_mutex.Lock() res, err := create_forum_stmt.Exec(forum_name, forum_desc, active, preset) if err != nil { return 0, err @@ -127,15 +119,18 @@ func create_forum(forum_name string, forum_desc string, active bool, preset stri fid = int(fid64) forums = append(forums, Forum{fid,forum_name,forum_desc,active,preset,0,"",0,"",0,""}) + forum_create_mutex.Unlock() return fid, nil } func delete_forum(fid int) error { + forum_update_mutex.Lock() _, err := delete_forum_stmt.Exec(fid) if err != nil { return err } forums[fid].Name = "" + forum_update_mutex.Unlock() return nil } diff --git a/forum_store.go b/forum_store.go new file mode 100644 index 00000000..f473373c --- /dev/null +++ b/forum_store.go @@ -0,0 +1,70 @@ +/* Work in progress. Check back later! */ +package main + +import "log" +import "errors" +import "database/sql" +import "./query_gen/lib" + +var err_noforum = errors.New("This forum doesn't exist") + +type ForumStore interface +{ + Get(int) (*Forum, error) + CascadeGet(int) (*Forum, error) + BypassGet(int) (*Forum, error) + //Update(Forum) error + //CascadeUpdate(Forum) error + //Delete(int) error + //CascadeDelete(int) error + //QuickCreate(string, string, bool, string) (*Forum, error) + Exists(int) bool +} + +type StaticForumStore struct +{ + get *sql.Stmt + get_all *sql.Stmt +} + +func NewStaticForumStore() *StaticForumStore { + get_stmt, err := qgen.Builder.SimpleSelect("forums","name, desc, active, preset, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","fid = ?","","") + if err != nil { + log.Fatal(err) + } + get_all_stmt, err := qgen.Builder.SimpleSelect("forums","fid, name, desc, active, preset, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","","fid ASC","") + if err != nil { + log.Fatal(err) + } + return &StaticForumStore{ + get: get_stmt, + get_all: get_all_stmt, + } +} + +func (sfs *StaticForumStore) Get(id int) (*Forum, error) { + if !((id <= forumCapCount) && (id >= 0) && forums[id].Name!="") { + return nil, err_noforum + } + return &forums[id], nil +} + +func (sfs *StaticForumStore) CascadeGet(id int) (*Forum, error) { + if !((id <= forumCapCount) && (id >= 0) && forums[id].Name!="") { + return nil, err_noforum + } + return &forums[id], nil +} + +func (sfs *StaticForumStore) BypassGet(id int) (*Forum, error) { + var forum Forum = Forum{ID:id} + err := sfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + if err != nil { + return nil, err + } + return &forum, nil +} + +func (sfs *StaticForumStore) Exists(id int) bool { + return (id <= forumCapCount) && (id >= 0) && forums[id].Name != "" +} diff --git a/gen_mysql.go b/gen_mysql.go index bb2c7a6c..1959f65b 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -37,6 +37,10 @@ var get_user_group_stmt *sql.Stmt var get_emails_by_user_stmt *sql.Stmt var get_topic_basic_stmt *sql.Stmt var get_activity_entry_stmt *sql.Stmt +var forum_entry_exists_stmt *sql.Stmt +var group_entry_exists_stmt *sql.Stmt +var get_topic_replies_offset_stmt *sql.Stmt +var get_forum_topics_offset_stmt *sql.Stmt var get_topic_list_stmt *sql.Stmt var get_topic_user_stmt *sql.Stmt var get_topic_by_reply_stmt *sql.Stmt @@ -297,6 +301,30 @@ func gen_mysql() (err error) { return err } + log.Print("Preparing forum_entry_exists statement.") + forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' ORDER BY fid ASC LIMIT 0,1") + if err != nil { + return err + } + + log.Print("Preparing group_entry_exists statement.") + group_entry_exists_stmt, err = db.Prepare("SELECT `gid` FROM `users_groups` WHERE `name` = '' ORDER BY gid ASC LIMIT 0,1") + if err != nil { + return err + } + + log.Print("Preparing get_topic_replies_offset statement.") + get_topic_replies_offset_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ? LIMIT ?,?") + if err != nil { + return err + } + + log.Print("Preparing get_forum_topics_offset statement.") + get_forum_topics_offset_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`topics`.`postCount`,`topics`.`likeCount`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC LIMIT ?,?") + if err != nil { + return err + } + log.Print("Preparing get_topic_list statement.") get_topic_list_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC") if err != nil { diff --git a/general_test.go b/general_test.go index d92dd6b4..951179fd 100644 --- a/general_test.go +++ b/general_test.go @@ -55,8 +55,8 @@ func gloinit() { } if cache_topicuser == CACHE_STATIC { - users = NewStaticUserStore(user_cache_capacity) - topics = NewStaticTopicStore(topic_cache_capacity) + users = NewMemoryUserStore(user_cache_capacity) + topics = NewMemoryTopicStore(topic_cache_capacity) } else { users = NewSqlUserStore() topics = NewSqlTopicStore() diff --git a/group.go b/group.go index 5c386281..b440d4f0 100644 --- a/group.go +++ b/group.go @@ -31,6 +31,7 @@ type Group struct CanSee []int // The IDs of the forums this group can see } +var group_create_mutex sync.Mutex func create_group(group_name string, tag string, is_admin bool, is_mod bool, is_banned bool) (int, error) { var gid int err := group_entry_exists_stmt.QueryRow().Scan(&gid) @@ -47,23 +48,24 @@ func create_group(group_name string, tag string, is_admin bool, is_mod bool, is_ if err != nil { return gid, err } - + groups[gid].Name = group_name groups[gid].Tag = tag groups[gid].Is_Banned = is_banned groups[gid].Is_Mod = is_mod groups[gid].Is_Admin = is_admin - + group_update_mutex.Unlock() return gid, nil } - + + group_create_mutex.Lock() var permstr string = "{}" res, err := create_group_stmt.Exec(group_name, tag, is_admin, is_mod, is_banned, permstr) if err != nil { return 0, err } - + gid64, err := res.LastInsertId() if err != nil { return 0, err @@ -73,7 +75,8 @@ func create_group(group_name string, tag string, is_admin bool, is_mod bool, is_ var blankForums []ForumPerms var blankIntList []int groups = append(groups, Group{gid,group_name,is_mod,is_admin,is_banned,tag,perms,[]byte(permstr),blankForums,blankIntList}) - + group_create_mutex.Unlock() + // Generate the forum permissions based on the presets... fdata := forums permupdate_mutex.Lock() @@ -88,7 +91,7 @@ func create_group(group_name string, tag string, is_admin bool, is_mod bool, is_ } else { thePreset = "members" } - + permmap := preset_to_permmap(forum.Preset) permitem := permmap[thePreset] permitem.Overrides = true @@ -101,7 +104,7 @@ func create_group(group_name string, tag string, is_admin bool, is_mod bool, is_ if err != nil { return gid, err } - + err = rebuild_forum_permissions(forum.ID) if err != nil { return gid, err diff --git a/main.go b/main.go index 94abe137..071345c4 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ var external_sites map[string]string = make(map[string]string) var groups []Group var forums []Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency var forum_perms map[int]map[int]ForumPerms // [gid][fid]Perms +var fstore ForumStore // :soon: var groupCapCount, forumCapCount int var static_files map[string]SFile = make(map[string]SFile) @@ -185,8 +186,8 @@ func main(){ } if cache_topicuser == CACHE_STATIC { - users = NewStaticUserStore(user_cache_capacity) - topics = NewStaticTopicStore(topic_cache_capacity) + users = NewMemoryUserStore(user_cache_capacity) + topics = NewMemoryTopicStore(topic_cache_capacity) } else { users = NewSqlUserStore() topics = NewSqlTopicStore() diff --git a/mysql.go b/mysql.go index 24eb58a9..892c0860 100644 --- a/mysql.go +++ b/mysql.go @@ -3,23 +3,14 @@ package main import "log" -import "strconv" +import "strings" import "database/sql" import _ "github.com/go-sql-driver/mysql" import "./query_gen/lib" -var db *sql.DB -var db_version string -var db_collation string = "utf8mb4_general_ci" - -var get_topic_replies_offset_stmt *sql.Stmt // I'll need to rewrite this one to stop it hard-coding the per page setting before moving it to the query generator -var get_forum_topics_offset_stmt *sql.Stmt var notify_watchers_stmt *sql.Stmt var get_activity_feed_by_watcher_stmt *sql.Stmt var get_activity_count_by_watcher_stmt *sql.Stmt - -var forum_entry_exists_stmt *sql.Stmt -var group_entry_exists_stmt *sql.Stmt var add_forum_perms_to_forum_admins_stmt *sql.Stmt var add_forum_perms_to_forum_staff_stmt *sql.Stmt var add_forum_perms_to_forum_members_stmt *sql.Stmt @@ -65,18 +56,6 @@ func _init_database() (err error) { return err } - log.Print("Preparing get_topic_replies_offset statement.") - get_topic_replies_offset_stmt, err = db.Prepare("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType from replies left join users on replies.createdBy = users.uid where tid = ? limit ?, " + strconv.Itoa(items_per_page)) - if err != nil { - return err - } - - log.Print("Preparing get_forum_topics_offset statement.") - get_forum_topics_offset_stmt, err = db.Prepare("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid WHERE topics.parentID = ? order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC limit ?, " + strconv.Itoa(items_per_page)) - if err != nil { - return err - } - log.Print("Preparing notify_watchers statement.") notify_watchers_stmt, err = db.Prepare("INSERT INTO activity_stream_matches(watcher, asid) SELECT activity_subscriptions.user, activity_stream.asid FROM activity_stream INNER JOIN activity_subscriptions ON activity_subscriptions.targetType = activity_stream.elementType and activity_subscriptions.targetID = activity_stream.elementID and activity_subscriptions.user != activity_stream.actor where asid = ?") if err != nil { @@ -95,18 +74,6 @@ func _init_database() (err error) { return err } - log.Print("Preparing forum_entry_exists statement.") - forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' order by fid asc limit 1") - if err != nil { - return err - } - - log.Print("Preparing group_entry_exists statement.") - group_entry_exists_stmt, err = db.Prepare("SELECT `gid` FROM `users_groups` WHERE `name` = '' order by gid asc limit 1") - if err != nil { - return err - } - log.Print("Preparing add_forum_perms_to_forum_admins statement.") add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 1") if err != nil { @@ -151,3 +118,8 @@ func _init_database() (err error) { return nil } + +// Temporary hack so that we can move all the raw queries out of the other files and into here +func topic_list_query(visible_fids []string) string { + return "select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid where parentID in("+strings.Join(visible_fids,",")+") order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC" +} diff --git a/query_gen/lib/builder.go b/query_gen/lib/builder.go index 571c2a8a..372e9860 100644 --- a/query_gen/lib/builder.go +++ b/query_gen/lib/builder.go @@ -28,8 +28,8 @@ func (build *builder) SetAdapter(name string) error { return nil } -func (build *builder) SimpleSelect(table string, columns string, where string, orderby string/*, offset int, maxCount int*/) (stmt *sql.Stmt, err error) { - res, err := build.adapter.SimpleSelect("_builder", table, columns, where, orderby /*, offset, maxCount*/) +func (build *builder) SimpleSelect(table string, columns string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) { + res, err := build.adapter.SimpleSelect("_builder", table, columns, where, orderby, limit) if err != nil { return stmt, err } diff --git a/query_gen/lib/mysql.go b/query_gen/lib/mysql.go index 068262fe..97be2b62 100644 --- a/query_gen/lib/mysql.go +++ b/query_gen/lib/mysql.go @@ -11,8 +11,6 @@ func init() { ) } - - type Mysql_Adapter struct { Name string @@ -210,7 +208,7 @@ func (adapter *Mysql_Adapter) Purge(name string, table string) (string, error) { return "DELETE FROM `" + table + "`", nil } -func (adapter *Mysql_Adapter) SimpleSelect(name string, table string, columns string, where string, orderby string/*, offset int, maxCount int*/) (string, error) { +func (adapter *Mysql_Adapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") } @@ -264,12 +262,16 @@ func (adapter *Mysql_Adapter) SimpleSelect(name string, table string, columns st querystr = querystr[0:len(querystr) - 1] } + if limit != "" { + querystr += " LIMIT " + limit + } + querystr = strings.TrimSpace(querystr) adapter.push_statement(name,querystr) return querystr, nil } -func (adapter *Mysql_Adapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) (string, error) { +func (adapter *Mysql_Adapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") } @@ -350,12 +352,16 @@ func (adapter *Mysql_Adapter) SimpleLeftJoin(name string, table1 string, table2 querystr = querystr[0:len(querystr) - 1] } + if limit != "" { + querystr += " LIMIT " + limit + } + querystr = strings.TrimSpace(querystr) adapter.push_statement(name,querystr) return querystr, nil } -func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) (string, error) { +func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") } @@ -436,12 +442,16 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 querystr = querystr[0:len(querystr) - 1] } + if limit != "" { + querystr += " LIMIT " + limit + } + querystr = strings.TrimSpace(querystr) adapter.push_statement(name,querystr) return querystr, nil } -func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where string/*, offset int, maxCount int*/) (string, error) { +func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") } @@ -472,6 +482,10 @@ func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where strin querystr = querystr[0:len(querystr) - 4] } + if limit != "" { + querystr += " LIMIT " + limit + } + querystr = strings.TrimSpace(querystr) adapter.push_statement(name,querystr) return querystr, nil diff --git a/query_gen/lib/querygen.go b/query_gen/lib/querygen.go index ec97d593..251e54fb 100644 --- a/query_gen/lib/querygen.go +++ b/query_gen/lib/querygen.go @@ -56,6 +56,11 @@ type DB_Setter struct { Expr []DB_Token // Simple expressions, the innards of functions are opaque for now. } +type DB_Limit struct { + Offset string // ? or int + MaxCount string // ? or int +} + type DB_Adapter interface { GetName() string SimpleInsert(string,string,string,string) (string, error) @@ -63,10 +68,10 @@ type DB_Adapter interface { SimpleUpdate(string,string,string,string) (string, error) SimpleDelete(string,string,string) (string, error) Purge(string,string) (string, error) - SimpleSelect(string,string,string,string,string/*,int,int*/) (string, error) - SimpleLeftJoin(string,string,string,string,string,string,string/*,int,int*/) (string, error) - SimpleInnerJoin(string,string,string,string,string,string,string/*,int,int*/) (string, error) - SimpleCount(string,string,string/*,int,int*/) (string, error) + SimpleSelect(string,string,string,string,string,string) (string, error) + SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error) + SimpleInnerJoin(string,string,string,string,string,string,string,string) (string, error) + SimpleCount(string,string,string,string) (string, error) Write() error // TO-DO: Add a simple query builder diff --git a/query_gen/lib/utils.go b/query_gen/lib/utils.go index c6dddbe9..b083219f 100644 --- a/query_gen/lib/utils.go +++ b/query_gen/lib/utils.go @@ -243,6 +243,17 @@ func _process_set(setstr string) (setter []DB_Setter) { return setter } +func _process_limit(limitstr string) (limiter DB_Limit) { + halves := strings.Split(limitstr,",") + if len(halves) == 2 { + limiter.Offset = halves[0] + limiter.MaxCount = halves[1] + } else { + limiter.MaxCount = halves[0] + } + return limiter +} + func _is_op_byte(char byte) bool { return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' } diff --git a/query_gen/main.go b/query_gen/main.go index 7c3861c9..9be176e3 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -51,92 +51,100 @@ func write_statements(adapter qgen.DB_Adapter) error { func write_selects(adapter qgen.DB_Adapter) error { // url_prefix and url_name will be removed from this query in a later commit - adapter.SimpleSelect("get_user","users","name, group, is_super_admin, avatar, message, url_prefix, url_name, level","uid = ?","") + adapter.SimpleSelect("get_user","users","name, group, is_super_admin, avatar, message, url_prefix, url_name, level","uid = ?","","") // Looking for get_topic? Your statement is in another castle - adapter.SimpleSelect("get_reply","replies","tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","") + adapter.SimpleSelect("get_reply","replies","tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","","") - adapter.SimpleSelect("get_user_reply","users_replies","uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress","rid = ?","") + adapter.SimpleSelect("get_user_reply","users_replies","uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress","rid = ?","","") - adapter.SimpleSelect("login","users","uid, name, password, salt","name = ?","") + adapter.SimpleSelect("login","users","uid, name, password, salt","name = ?","","") - adapter.SimpleSelect("get_password","users","password,salt","uid = ?","") + adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","") - adapter.SimpleSelect("username_exists","users","name","name = ?","") + adapter.SimpleSelect("username_exists","users","name","name = ?","","") - adapter.SimpleSelect("get_settings","settings","name, content, type","","") + adapter.SimpleSelect("get_settings","settings","name, content, type","","","") - adapter.SimpleSelect("get_setting","settings","content, type","name = ?","") + adapter.SimpleSelect("get_setting","settings","content, type","name = ?","","") - adapter.SimpleSelect("get_full_setting","settings","name, type, constraints","name = ?","") + adapter.SimpleSelect("get_full_setting","settings","name, type, constraints","name = ?","","") - adapter.SimpleSelect("get_full_settings","settings","name, content, type, constraints","","") + adapter.SimpleSelect("get_full_settings","settings","name, content, type, constraints","","","") - adapter.SimpleSelect("get_groups","users_groups","gid, name, permissions, is_mod, is_admin, is_banned, tag","","") + adapter.SimpleSelect("get_groups","users_groups","gid, name, permissions, is_mod, is_admin, is_banned, tag","","","") - adapter.SimpleSelect("get_forums","forums","fid, name, desc, active, preset, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","","fid ASC") + adapter.SimpleSelect("get_forums","forums","fid, name, desc, active, preset, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","","fid ASC","") - adapter.SimpleSelect("get_forums_permissions","forums_permissions","gid, fid, permissions","","gid ASC, fid ASC") + adapter.SimpleSelect("get_forums_permissions","forums_permissions","gid, fid, permissions","","gid ASC, fid ASC","") - adapter.SimpleSelect("get_plugins","plugins","uname, active","","") + adapter.SimpleSelect("get_plugins","plugins","uname, active","","","") - adapter.SimpleSelect("get_themes","themes","uname, default","","") + adapter.SimpleSelect("get_themes","themes","uname, default","","","") - adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","") + adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","","") - adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","") + adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","") - adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","") + adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","","") - adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","") + adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","") - adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","") + adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","","") - adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","") + adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","","") - adapter.SimpleSelect("get_user_reply_uid","users_replies","uid","rid = ?","") + adapter.SimpleSelect("get_user_reply_uid","users_replies","uid","rid = ?","","") - adapter.SimpleSelect("has_liked_topic","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'topics'","") + adapter.SimpleSelect("has_liked_topic","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'topics'","","") - adapter.SimpleSelect("has_liked_reply","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'replies'","") + adapter.SimpleSelect("has_liked_reply","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'replies'","","") - adapter.SimpleSelect("get_user_name","users","name","uid = ?","") + adapter.SimpleSelect("get_user_name","users","name","uid = ?","","") - adapter.SimpleSelect("get_user_rank","users","group, is_super_admin","uid = ?","") + adapter.SimpleSelect("get_user_rank","users","group, is_super_admin","uid = ?","","") - adapter.SimpleSelect("get_user_active","users","active","uid = ?","") + adapter.SimpleSelect("get_user_active","users","active","uid = ?","","") - adapter.SimpleSelect("get_user_group","users","group","uid = ?","") + adapter.SimpleSelect("get_user_group","users","group","uid = ?","","") - adapter.SimpleSelect("get_emails_by_user","emails","email, validated, token","uid = ?","") + adapter.SimpleSelect("get_emails_by_user","emails","email, validated, token","uid = ?","","") - adapter.SimpleSelect("get_topic_basic","topics","title, content","tid = ?","") + adapter.SimpleSelect("get_topic_basic","topics","title, content","tid = ?","","") - adapter.SimpleSelect("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","") + adapter.SimpleSelect("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","","") + + adapter.SimpleSelect("forum_entry_exists","forums","fid","name = ''","fid ASC","0,1") // Is '' empty? + + adapter.SimpleSelect("group_entry_exists","users_groups","gid","name = ''","gid ASC","0,1") // Is '' empty? return nil } func write_left_joins(adapter qgen.DB_Adapter) error { - adapter.SimpleLeftJoin("get_topic_list","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC") + adapter.SimpleLeftJoin("get_topic_replies_offset","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType","replies.createdBy = users.uid","tid = ?","","?,?") - adapter.SimpleLeftJoin("get_topic_user","topics","users","topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level","topics.createdBy = users.uid","tid = ?","") + adapter.SimpleLeftJoin("get_forum_topics_offset","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","?,?") - adapter.SimpleLeftJoin("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data","replies.tid = topics.tid","rid = ?","") + adapter.SimpleLeftJoin("get_topic_list","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","") - adapter.SimpleLeftJoin("get_topic_replies","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress","replies.createdBy = users.uid","tid = ?","") + adapter.SimpleLeftJoin("get_topic_user","topics","users","topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level","topics.createdBy = users.uid","tid = ?","","") - adapter.SimpleLeftJoin("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc") + adapter.SimpleLeftJoin("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data","replies.tid = topics.tid","rid = ?","","") - adapter.SimpleLeftJoin("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","") + adapter.SimpleLeftJoin("get_topic_replies","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress","replies.createdBy = users.uid","tid = ?","","") + + adapter.SimpleLeftJoin("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc","") + + adapter.SimpleLeftJoin("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","","") return nil } func write_inner_joins(adapter qgen.DB_Adapter) error { - adapter.SimpleInnerJoin("get_watchers","activity_stream","activity_subscriptions","activity_subscriptions.user","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","") + adapter.SimpleInnerJoin("get_watchers","activity_stream","activity_subscriptions","activity_subscriptions.user","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","","") return nil } @@ -283,7 +291,7 @@ func write_deletes(adapter qgen.DB_Adapter) error { } func write_simple_counts(adapter qgen.DB_Adapter) error { - adapter.SimpleCount("report_exists","topics","data = ? and data != '' and parentID = 1") + adapter.SimpleCount("report_exists","topics","data = ? and data != '' and parentID = 1","") return nil } diff --git a/routes.go b/routes.go index 3fdd28d0..9972d246 100644 --- a/routes.go +++ b/routes.go @@ -111,8 +111,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){ } var topicList []TopicsRow - rows, err := db.Query("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid where parentID in("+strings.Join(fidList,",")+") order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC") - //rows, err := get_topic_list_stmt.Query() + rows, err := db.Query(topic_list_query(fidList)) if err != nil { InternalError(err,w,r) return @@ -201,7 +200,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, sfid string){ } else { page = 1 } - rows, err := get_forum_topics_offset_stmt.Query(fid,offset) + rows, err := get_forum_topics_offset_stmt.Query(fid,offset,items_per_page) if err != nil { InternalError(err,w,r) return @@ -364,7 +363,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ } // Get the replies.. - rows, err := get_topic_replies_offset_stmt.Query(topic.ID, offset) + rows, err := get_topic_replies_offset_stmt.Query(topic.ID, offset, items_per_page) if err == sql.ErrNoRows { LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.",w,r,user) return diff --git a/topic_store.go b/topic_store.go index aa0895ce..87322463 100644 --- a/topic_store.go +++ b/topic_store.go @@ -23,7 +23,7 @@ type TopicStore interface { GetCapacity() int } -type StaticTopicStore struct { +type MemoryTopicStore struct { items map[int]*Topic length int capacity int @@ -31,19 +31,19 @@ type StaticTopicStore struct { sync.RWMutex } -func NewStaticTopicStore(capacity int) *StaticTopicStore { - stmt, err := qgen.Builder.SimpleSelect("topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","") +func NewMemoryTopicStore(capacity int) *MemoryTopicStore { + stmt, err := qgen.Builder.SimpleSelect("topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","","") if err != nil { log.Fatal(err) } - return &StaticTopicStore{ + return &MemoryTopicStore{ items:make(map[int]*Topic), capacity:capacity, get:stmt, } } -func (sts *StaticTopicStore) Get(id int) (*Topic, error) { +func (sts *MemoryTopicStore) Get(id int) (*Topic, error) { sts.RLock() item, ok := sts.items[id] sts.RUnlock() @@ -53,7 +53,7 @@ func (sts *StaticTopicStore) Get(id int) (*Topic, error) { return item, sql.ErrNoRows } -func (sts *StaticTopicStore) GetUnsafe(id int) (*Topic, error) { +func (sts *MemoryTopicStore) GetUnsafe(id int) (*Topic, error) { item, ok := sts.items[id] if ok { return item, nil @@ -61,7 +61,7 @@ func (sts *StaticTopicStore) GetUnsafe(id int) (*Topic, error) { return item, sql.ErrNoRows } -func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) { +func (sts *MemoryTopicStore) CascadeGet(id int) (*Topic, error) { sts.RLock() topic, ok := sts.items[id] sts.RUnlock() @@ -77,13 +77,13 @@ func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) { return topic, err } -func (sts *StaticTopicStore) BypassGet(id int) (*Topic, error) { +func (sts *MemoryTopicStore) BypassGet(id int) (*Topic, error) { topic := &Topic{ID:id} err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data) return topic, err } -func (sts *StaticTopicStore) Load(id int) error { +func (sts *MemoryTopicStore) Load(id int) error { topic := &Topic{ID:id} err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data) if err == nil { @@ -94,7 +94,7 @@ func (sts *StaticTopicStore) Load(id int) error { return err } -func (sts *StaticTopicStore) Set(item *Topic) error { +func (sts *MemoryTopicStore) Set(item *Topic) error { sts.Lock() _, ok := sts.items[item.ID] if ok { @@ -110,7 +110,7 @@ func (sts *StaticTopicStore) Set(item *Topic) error { return nil } -func (sts *StaticTopicStore) Add(item *Topic) error { +func (sts *MemoryTopicStore) Add(item *Topic) error { if sts.length >= sts.capacity { return ErrStoreCapacityOverflow } @@ -121,7 +121,7 @@ func (sts *StaticTopicStore) Add(item *Topic) error { return nil } -func (sts *StaticTopicStore) AddUnsafe(item *Topic) error { +func (sts *MemoryTopicStore) AddUnsafe(item *Topic) error { if sts.length >= sts.capacity { return ErrStoreCapacityOverflow } @@ -130,7 +130,7 @@ func (sts *StaticTopicStore) AddUnsafe(item *Topic) error { return nil } -func (sts *StaticTopicStore) Remove(id int) error { +func (sts *MemoryTopicStore) Remove(id int) error { sts.Lock() delete(sts.items,id) sts.Unlock() @@ -138,40 +138,35 @@ func (sts *StaticTopicStore) Remove(id int) error { return nil } -func (sts *StaticTopicStore) RemoveUnsafe(id int) error { +func (sts *MemoryTopicStore) RemoveUnsafe(id int) error { delete(sts.items,id) sts.length-- return nil } -func (sts *StaticTopicStore) AddLastTopic(item *Topic, fid int) error { +func (sts *MemoryTopicStore) AddLastTopic(item *Topic, fid int) error { // Coming Soon... return nil } -func (sts *StaticTopicStore) GetLength() int { +func (sts *MemoryTopicStore) GetLength() int { return sts.length } -func (sts *StaticTopicStore) SetCapacity(capacity int) { +func (sts *MemoryTopicStore) SetCapacity(capacity int) { sts.capacity = capacity } -func (sts *StaticTopicStore) GetCapacity() int { +func (sts *MemoryTopicStore) GetCapacity() int { return sts.capacity } -//type DynamicTopicStore struct { -// items_expiries list.List -// items map[int]*Topic -//} - type SqlTopicStore struct { get *sql.Stmt } func NewSqlTopicStore() *SqlTopicStore { - stmt, err := qgen.Builder.SimpleSelect("topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","") + stmt, err := qgen.Builder.SimpleSelect("topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","","") if err != nil { log.Fatal(err) } diff --git a/user.go b/user.go index 3fd1d222..b2a7fa98 100644 --- a/user.go +++ b/user.go @@ -11,6 +11,7 @@ import ( ) var guest_user User = User{ID:0,Group:6,Perms:GuestPerms} +var SimpleSessionCheck func(http.ResponseWriter, *http.Request) (User,bool) = _simple_session_check type User struct { @@ -145,7 +146,7 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, noticeList return user, noticeList, success } -func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (User,bool) { +func _simple_session_check(w http.ResponseWriter, r *http.Request) (User,bool) { // Are there any session cookies..? cookie, err := r.Cookie("uid") if err != nil { diff --git a/user_store.go b/user_store.go index 95ca6662..42c8e3f2 100644 --- a/user_store.go +++ b/user_store.go @@ -26,7 +26,7 @@ type UserStore interface { GetCapacity() int } -type StaticUserStore struct { +type MemoryUserStore struct { items map[int]*User length int capacity int @@ -34,19 +34,19 @@ type StaticUserStore struct { sync.RWMutex } -func NewStaticUserStore(capacity int) *StaticUserStore { - stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","") +func NewMemoryUserStore(capacity int) *MemoryUserStore { + stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","","") if err != nil { log.Fatal(err) } - return &StaticUserStore{ + return &MemoryUserStore{ items:make(map[int]*User), capacity:capacity, get:stmt, } } -func (sus *StaticUserStore) Get(id int) (*User, error) { +func (sus *MemoryUserStore) Get(id int) (*User, error) { sus.RLock() item, ok := sus.items[id] sus.RUnlock() @@ -56,7 +56,7 @@ func (sus *StaticUserStore) Get(id int) (*User, error) { return item, sql.ErrNoRows } -func (sus *StaticUserStore) GetUnsafe(id int) (*User, error) { +func (sus *MemoryUserStore) GetUnsafe(id int) (*User, error) { item, ok := sus.items[id] if ok { return item, nil @@ -64,7 +64,7 @@ func (sus *StaticUserStore) GetUnsafe(id int) (*User, error) { return item, sql.ErrNoRows } -func (sus *StaticUserStore) CascadeGet(id int) (*User, error) { +func (sus *MemoryUserStore) CascadeGet(id int) (*User, error) { sus.RLock() user, ok := sus.items[id] sus.RUnlock() @@ -90,7 +90,7 @@ func (sus *StaticUserStore) CascadeGet(id int) (*User, error) { return user, err } -func (sus *StaticUserStore) BypassGet(id int) (*User, error) { +func (sus *MemoryUserStore) BypassGet(id int) (*User, error) { user := &User{ID:id,Loggedin:true} err := sus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) @@ -106,7 +106,7 @@ func (sus *StaticUserStore) BypassGet(id int) (*User, error) { return user, err } -func (sus *StaticUserStore) Load(id int) error { +func (sus *MemoryUserStore) Load(id int) error { user := &User{ID:id,Loggedin:true} err := sus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) if err != nil { @@ -127,7 +127,7 @@ func (sus *StaticUserStore) Load(id int) error { return nil } -func (sus *StaticUserStore) Set(item *User) error { +func (sus *MemoryUserStore) Set(item *User) error { sus.Lock() user, ok := sus.items[item.ID] if ok { @@ -144,7 +144,7 @@ func (sus *StaticUserStore) Set(item *User) error { return nil } -func (sus *StaticUserStore) Add(item *User) error { +func (sus *MemoryUserStore) Add(item *User) error { if sus.length >= sus.capacity { return ErrStoreCapacityOverflow } @@ -155,7 +155,7 @@ func (sus *StaticUserStore) Add(item *User) error { return nil } -func (sus *StaticUserStore) AddUnsafe(item *User) error { +func (sus *MemoryUserStore) AddUnsafe(item *User) error { if sus.length >= sus.capacity { return ErrStoreCapacityOverflow } @@ -164,7 +164,7 @@ func (sus *StaticUserStore) AddUnsafe(item *User) error { return nil } -func (sus *StaticUserStore) Remove(id int) error { +func (sus *MemoryUserStore) Remove(id int) error { sus.Lock() delete(sus.items,id) sus.Unlock() @@ -172,35 +172,30 @@ func (sus *StaticUserStore) Remove(id int) error { return nil } -func (sus *StaticUserStore) RemoveUnsafe(id int) error { +func (sus *MemoryUserStore) RemoveUnsafe(id int) error { delete(sus.items,id) sus.length-- return nil } -func (sus *StaticUserStore) GetLength() int { +func (sus *MemoryUserStore) GetLength() int { return sus.length } -func (sus *StaticUserStore) SetCapacity(capacity int) { +func (sus *MemoryUserStore) SetCapacity(capacity int) { sus.capacity = capacity } -func (sus *StaticUserStore) GetCapacity() int { +func (sus *MemoryUserStore) GetCapacity() int { return sus.capacity } -//type DynamicUserStore struct { -// items_expiries list.List -// items map[int]*User -//} - type SqlUserStore struct { get *sql.Stmt } func NewSqlUserStore() *SqlUserStore { - stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","") + stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","","") if err != nil { log.Fatal(err) }