From 1ccd4479ae9608d11617b7de033925141f180892 Mon Sep 17 00:00:00 2001 From: Azareal Date: Thu, 15 Jun 2017 12:40:35 +0100 Subject: [PATCH] Added support for offsets and max counts to the query generator. Early draft of the ForumStore. Not much to see here yet! Fixed a race condition in forum creation. Fixed a race condition in group creation. Fixed a race condition in forum deletion. Moved four queries to the query generator. Renamed StaticTopicStore to MemoryTopicStore. Renamed StaticUserStore to MemoryUserStore. SimpleSessionCheck is now pluggable. We no longer have any raw queries outside mysql.go or the query generator, yay. --- database.go | 6 +++ forum.go | 15 +++---- forum_store.go | 70 +++++++++++++++++++++++++++++++ gen_mysql.go | 28 +++++++++++++ general_test.go | 4 +- group.go | 17 ++++---- main.go | 5 ++- mysql.go | 40 +++--------------- query_gen/lib/builder.go | 4 +- query_gen/lib/mysql.go | 26 +++++++++--- query_gen/lib/querygen.go | 13 ++++-- query_gen/lib/utils.go | 11 +++++ query_gen/main.go | 86 +++++++++++++++++++++------------------ routes.go | 7 ++-- topic_store.go | 43 +++++++++----------- user.go | 3 +- user_store.go | 41 ++++++++----------- 17 files changed, 261 insertions(+), 158 deletions(-) create mode 100644 forum_store.go 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) }