From af09013a259ebc01f1b5fa83bfc4e570e1c0d0e0 Mon Sep 17 00:00:00 2001 From: Azareal Date: Tue, 13 Jun 2017 08:12:58 +0100 Subject: [PATCH] Refactored query_gen into a library. Moved topic store and user store into their own files. Tweaked topic store to stop it accessing globals. --- cache.go | 3 - mysql.go | 4 +- query_gen/lib/builder.go | 7 + query_gen/{ => lib}/mysql.go | 104 +++++++------ query_gen/lib/querygen.go | 81 ++++++++++ query_gen/{ => lib}/utils.go | 8 +- query_gen/main.go | 273 +++++++++++++-------------------- topic.go | 218 -------------------------- topic_store.go | 223 +++++++++++++++++++++++++++ user.go | 281 ---------------------------------- user_store.go | 287 +++++++++++++++++++++++++++++++++++ 11 files changed, 771 insertions(+), 718 deletions(-) create mode 100644 query_gen/lib/builder.go rename query_gen/{ => lib}/mysql.go (78%) create mode 100644 query_gen/lib/querygen.go rename query_gen/{ => lib}/utils.go (99%) create mode 100644 topic_store.go create mode 100644 user_store.go diff --git a/cache.go b/cache.go index 4e09310f..80ff68aa 100644 --- a/cache.go +++ b/cache.go @@ -7,9 +7,6 @@ const CACHE_SQL int = 2 var ErrStoreCapacityOverflow = errors.New("This datastore has already reached it's max capacity") -var users UserStore -var topics TopicStore - type DataStore interface { Load(id int) error Get(id int) (interface{}, error) diff --git a/mysql.go b/mysql.go index c2d879fd..258ce199 100644 --- a/mysql.go +++ b/mysql.go @@ -2,12 +2,12 @@ // +build !pgsql !sqlite !mssql package main -import "database/sql" -import _ "github.com/go-sql-driver/mysql" import "log" import "fmt" import "strconv" import "encoding/json" +import "database/sql" +import _ "github.com/go-sql-driver/mysql" var db *sql.DB var db_version string diff --git a/query_gen/lib/builder.go b/query_gen/lib/builder.go new file mode 100644 index 00000000..62384e63 --- /dev/null +++ b/query_gen/lib/builder.go @@ -0,0 +1,7 @@ +/* WIP Under Construction */ +package qgen + +type Builder struct +{ + +} diff --git a/query_gen/mysql.go b/query_gen/lib/mysql.go similarity index 78% rename from query_gen/mysql.go rename to query_gen/lib/mysql.go index 0f83ae7b..d0b7f97e 100644 --- a/query_gen/mysql.go +++ b/query_gen/lib/mysql.go @@ -1,26 +1,38 @@ /* WIP Under Construction */ -package main +package qgen //import "fmt" import "strings" import "errors" func init() { - db_registry = append(db_registry,&Mysql_Adapter{Name:"mysql"}) + DB_Registry = append(DB_Registry, + &Mysql_Adapter{Name:"mysql",Buffer:make(map[string]string)}, + ) } + + type Mysql_Adapter struct { Name string - Stmts string - Body string + Buffer map[string]string + BufferOrder []string // Map iteration order is random, so we need this to track the order, so we don't get huge diffs every commit } -func (adapter *Mysql_Adapter) get_name() string { +func (adapter *Mysql_Adapter) GetName() string { return adapter.Name } -func (adapter *Mysql_Adapter) simple_insert(name string, table string, columns string, fields string) error { +func (adapter *Mysql_Adapter) GetStmt(name string) string { + return adapter.Buffer[name] +} + +func (adapter *Mysql_Adapter) GetStmts() map[string]string { + return adapter.Buffer +} + +func (adapter *Mysql_Adapter) SimpleInsert(name string, table string, columns string, fields string) error { if name == "" { return errors.New("You need a name for this statement") } @@ -28,10 +40,10 @@ func (adapter *Mysql_Adapter) simple_insert(name string, table string, columns s return errors.New("You need a name for this table") } if len(columns) == 0 { - return errors.New("No columns found for simple_insert") + return errors.New("No columns found for SimpleInsert") } if len(fields) == 0 { - return errors.New("No input data found for simple_insert") + return errors.New("No input data found for SimpleInsert") } var querystr string = "INSERT INTO `" + table + "`(" @@ -54,11 +66,11 @@ func (adapter *Mysql_Adapter) simple_insert(name string, table string, columns s } querystr = querystr[0:len(querystr) - 1] - adapter.write_statement(name,querystr + ")") + adapter.push_statement(name,querystr + ")") return nil } -func (adapter *Mysql_Adapter) simple_replace(name string, table string, columns string, fields string) error { +func (adapter *Mysql_Adapter) SimpleReplace(name string, table string, columns string, fields string) error { if name == "" { return errors.New("You need a name for this statement") } @@ -66,10 +78,10 @@ func (adapter *Mysql_Adapter) simple_replace(name string, table string, columns return errors.New("You need a name for this table") } if len(columns) == 0 { - return errors.New("No columns found for simple_insert") + return errors.New("No columns found for SimpleInsert") } if len(fields) == 0 { - return errors.New("No input data found for simple_insert") + return errors.New("No input data found for SimpleInsert") } var querystr string = "REPLACE INTO `" + table + "`(" @@ -92,11 +104,11 @@ func (adapter *Mysql_Adapter) simple_replace(name string, table string, columns } querystr = querystr[0:len(querystr) - 1] - adapter.write_statement(name,querystr + ")") + adapter.push_statement(name,querystr + ")") return nil } -func (adapter *Mysql_Adapter) simple_update(name string, table string, set string, where string) error { +func (adapter *Mysql_Adapter) SimpleUpdate(name string, table string, set string, where string) error { if name == "" { return errors.New("You need a name for this statement") } @@ -148,11 +160,11 @@ func (adapter *Mysql_Adapter) simple_update(name string, table string, set strin querystr = querystr[0:len(querystr) - 4] } - adapter.write_statement(name,querystr) + adapter.push_statement(name,querystr) return nil } -func (adapter *Mysql_Adapter) simple_delete(name string, table string, where string) error { +func (adapter *Mysql_Adapter) SimpleDelete(name string, table string, where string) error { if name == "" { return errors.New("You need a name for this statement") } @@ -183,23 +195,23 @@ func (adapter *Mysql_Adapter) simple_delete(name string, table string, where str } querystr = querystr[0:len(querystr) - 4] - adapter.write_statement(name,strings.TrimSpace(querystr)) + adapter.push_statement(name,strings.TrimSpace(querystr)) return nil } // We don't want to accidentally wipe tables, so we'll have a seperate method for purging tables instead -func (adapter *Mysql_Adapter) purge(name string, table string) error { +func (adapter *Mysql_Adapter) Purge(name string, table string) error { if name == "" { return errors.New("You need a name for this statement") } if table == "" { return errors.New("You need a name for this table") } - adapter.write_statement(name,"DELETE FROM `" + table + "`") + adapter.push_statement(name,"DELETE FROM `" + table + "`") return nil } -func (adapter *Mysql_Adapter) simple_select(name string, table string, columns string, where string, orderby string/*, offset int, maxCount int*/) error { +func (adapter *Mysql_Adapter) SimpleSelect(name string, table string, columns string, where string, orderby string/*, offset int, maxCount int*/) error { if name == "" { return errors.New("You need a name for this statement") } @@ -207,7 +219,7 @@ func (adapter *Mysql_Adapter) simple_select(name string, table string, columns s return errors.New("You need a name for this table") } if len(columns) == 0 { - return errors.New("No columns found for simple_select") + return errors.New("No columns found for SimpleSelect") } // Slice up the user friendly strings into something easier to process @@ -254,11 +266,11 @@ func (adapter *Mysql_Adapter) simple_select(name string, table string, columns s querystr = querystr[0:len(querystr) - 1] } - adapter.write_statement(name,strings.TrimSpace(querystr)) + adapter.push_statement(name,strings.TrimSpace(querystr)) return nil } -func (adapter *Mysql_Adapter) simple_left_join(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) error { +func (adapter *Mysql_Adapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) error { if name == "" { return errors.New("You need a name for this statement") } @@ -269,10 +281,10 @@ func (adapter *Mysql_Adapter) simple_left_join(name string, table1 string, table return errors.New("You need a name for the right table") } if len(columns) == 0 { - return errors.New("No columns found for simple_left_join") + return errors.New("No columns found for SimpleLeftJoin") } if len(joiners) == 0 { - return errors.New("No joiners found for simple_left_join") + return errors.New("No joiners found for SimpleLeftJoin") } var querystr string = "SELECT " @@ -339,11 +351,11 @@ func (adapter *Mysql_Adapter) simple_left_join(name string, table1 string, table querystr = querystr[0:len(querystr) - 1] } - adapter.write_statement(name,strings.TrimSpace(querystr)) + adapter.push_statement(name,strings.TrimSpace(querystr)) return nil } -func (adapter *Mysql_Adapter) simple_inner_join(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) error { +func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) error { if name == "" { return errors.New("You need a name for this statement") } @@ -354,10 +366,10 @@ func (adapter *Mysql_Adapter) simple_inner_join(name string, table1 string, tabl return errors.New("You need a name for the right table") } if len(columns) == 0 { - return errors.New("No columns found for simple_inner_join") + return errors.New("No columns found for SimpleInnerJoin") } if len(joiners) == 0 { - return errors.New("No joiners found for simple_inner_join") + return errors.New("No joiners found for SimpleInnerJoin") } var querystr string = "SELECT " @@ -424,11 +436,24 @@ func (adapter *Mysql_Adapter) simple_inner_join(name string, table1 string, tabl querystr = querystr[0:len(querystr) - 1] } - adapter.write_statement(name,strings.TrimSpace(querystr)) + adapter.push_statement(name,strings.TrimSpace(querystr)) return nil } -func (adapter *Mysql_Adapter) write() error { +func (adapter *Mysql_Adapter) Write() error { + var stmts, body string + + for _, name := range adapter.BufferOrder { + stmts += "var " + name + "_stmt *sql.Stmt\n" + body += ` + log.Print("Preparing ` + name + ` statement.") + ` + name + `_stmt, err = db.Prepare("` + adapter.Buffer[name] + `") + if err != nil { + return err + } + ` + } + out := `// Code generated by. DO NOT EDIT. /* This file was generated by Gosora's Query Generator. The thing above is to tell GH this file is generated. */ // +build !pgsql !sqlite !mssql @@ -437,12 +462,12 @@ package main import "log" import "database/sql" -` + adapter.Stmts + ` +` + stmts + ` func gen_mysql() (err error) { if debug { log.Print("Building the generated statements") } -` + adapter.Body + ` +` + body + ` return nil } ` @@ -450,14 +475,7 @@ func gen_mysql() (err error) { } // Internal method, not exposed in the interface -func (adapter *Mysql_Adapter) write_statement(name string, querystr string ) { - adapter.Stmts += "var " + name + "_stmt *sql.Stmt\n" - - adapter.Body += ` - log.Print("Preparing ` + name + ` statement.") - ` + name + `_stmt, err = db.Prepare("` + querystr + `") - if err != nil { - return err - } - ` +func (adapter *Mysql_Adapter) push_statement(name string, querystr string ) { + adapter.Buffer[name] = querystr + adapter.BufferOrder = append(adapter.BufferOrder,name) } diff --git a/query_gen/lib/querygen.go b/query_gen/lib/querygen.go new file mode 100644 index 00000000..32eef036 --- /dev/null +++ b/query_gen/lib/querygen.go @@ -0,0 +1,81 @@ +/* WIP Under Construction */ +package qgen + +import "errors" + +var DB_Registry []DB_Adapter +var No_Adapter = errors.New("This adapter doesn't exist") + +type DB_Column struct +{ + Table string + Left string // Could be a function or a column, so I'm naming this Left + Alias string // aka AS Blah, if it's present + Type string // function or column +} + +type DB_Field struct +{ + Name string + Type string +} + +type DB_Where struct +{ + LeftTable string + LeftColumn string + RightTable string + RightColumn string + Operator string + LeftType string + RightType string +} + +type DB_Joiner struct +{ + LeftTable string + LeftColumn string + RightTable string + RightColumn string + Operator string +} + +type DB_Order struct +{ + Column string + Order string +} + +type DB_Token struct { + Contents string + Type string // function, operator, column, number, string, substitute +} + +type DB_Setter struct { + Column string + Expr []DB_Token // Simple expressions, the innards of functions are opaque for now. +} + +type DB_Adapter interface { + GetName() string + SimpleInsert(string,string,string,string) error + SimpleReplace(string,string,string,string) error + SimpleUpdate(string,string,string,string) error + SimpleDelete(string,string,string) error + Purge(string,string) error + SimpleSelect(string,string,string,string,string/*,int,int*/) error + SimpleLeftJoin(string,string,string,string,string,string,string/*,int,int*/) error + SimpleInnerJoin(string,string,string,string,string,string,string/*,int,int*/) error + Write() error + + // TO-DO: Add a simple query builder +} + +func GetAdapter(name string) (adap DB_Adapter, err error) { + for _, adapter := range DB_Registry { + if adapter.GetName() == name { + return adapter, nil + } + } + return adap, No_Adapter +} diff --git a/query_gen/utils.go b/query_gen/lib/utils.go similarity index 99% rename from query_gen/utils.go rename to query_gen/lib/utils.go index 18434459..0f2d57dc 100644 --- a/query_gen/utils.go +++ b/query_gen/lib/utils.go @@ -1,10 +1,16 @@ /* WIP Under Construction */ -package main +package qgen //import "fmt" import "strings" import "os" +type _statement struct +{ + Name string + Body string +} + func _process_columns(colstr string) (columns []DB_Column) { if colstr == "" { return columns diff --git a/query_gen/main.go b/query_gen/main.go index bd1ea2b5..ba8a9377 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -2,85 +2,18 @@ package main import "log" - -var db_registry []DB_Adapter -var blank_order []DB_Order - -type DB_Column struct -{ - Table string - Left string // Could be a function or a column, so I'm naming this Left - Alias string // aka AS Blah, if it's present - Type string // function or column -} - -type DB_Field struct -{ - Name string - Type string -} - -type DB_Where struct -{ - LeftTable string - LeftColumn string - RightTable string - RightColumn string - Operator string - LeftType string - RightType string -} - -type DB_Joiner struct -{ - LeftTable string - LeftColumn string - RightTable string - RightColumn string - Operator string -} - -type DB_Order struct -{ - Column string - Order string -} - -type DB_Token struct { - Contents string - Type string // function, operator, column, number, string, substitute -} - -type DB_Setter struct { - Column string - Expr []DB_Token // Simple expressions, the innards of functions are opaque for now. -} - -type DB_Adapter interface { - get_name() string - simple_insert(string,string,string,string) error - simple_replace(string,string,string,string) error - simple_update(string,string,string,string) error - simple_delete(string,string,string) error - purge(string,string) error - simple_select(string,string,string,string,string/*,int,int*/) error - simple_left_join(string,string,string,string,string,string,string/*,int,int*/) error - simple_inner_join(string,string,string,string,string,string,string/*,int,int*/) error - write() error - - // TO-DO: Add a simple query builder -} +import "./lib" func main() { log.Println("Running the query generator") - for _, adapter := range db_registry { - log.Println("Building the queries for the " + adapter.get_name() + " adapter") + for _, adapter := range qgen.DB_Registry { + log.Println("Building the queries for the " + adapter.GetName() + " adapter") write_statements(adapter) - adapter.write() + adapter.Write() } } -func write_statements(adapter DB_Adapter) error { +func write_statements(adapter qgen.DB_Adapter) error { err := write_selects(adapter) if err != nil { return err @@ -112,219 +45,219 @@ func write_statements(adapter DB_Adapter) error { return nil } -func write_selects(adapter 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.simple_select("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 = ?","") - adapter.simple_select("get_full_user","users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","") + adapter.SimpleSelect("get_full_user","users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","") - adapter.simple_select("get_topic","topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","") + adapter.SimpleSelect("get_topic","topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","") - adapter.simple_select("get_reply","replies","content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","") + adapter.SimpleSelect("get_reply","replies","content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","") - adapter.simple_select("login","users","uid, name, password, salt","name = ?","") + adapter.SimpleSelect("login","users","uid, name, password, salt","name = ?","") - adapter.simple_select("get_password","users","password,salt","uid = ?","") + adapter.SimpleSelect("get_password","users","password,salt","uid = ?","") - adapter.simple_select("username_exists","users","name","name = ?","") + adapter.SimpleSelect("username_exists","users","name","name = ?","") - adapter.simple_select("get_settings","settings","name, content, type","","") + adapter.SimpleSelect("get_settings","settings","name, content, type","","") - adapter.simple_select("get_setting","settings","content, type","name = ?","") + adapter.SimpleSelect("get_setting","settings","content, type","name = ?","") - adapter.simple_select("get_full_setting","settings","name, type, constraints","name = ?","") + adapter.SimpleSelect("get_full_setting","settings","name, type, constraints","name = ?","") - adapter.simple_select("is_plugin_active","plugins","active","uname = ?","") + adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","") - adapter.simple_select("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.simple_select("is_theme_default","themes","default","uname = ?","") + adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","") - adapter.simple_select("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","") + adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","") - adapter.simple_select("get_reply_tid","replies","tid","rid = ?","") + adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","") - adapter.simple_select("get_topic_fid","topics","parentID","tid = ?","") + adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","") - adapter.simple_select("get_user_reply_uid","users_replies","uid","rid = ?","") + adapter.SimpleSelect("get_user_reply_uid","users_replies","uid","rid = ?","") - adapter.simple_select("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.simple_select("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.simple_select("get_user_name","users","name","uid = ?","") + adapter.SimpleSelect("get_user_name","users","name","uid = ?","") - adapter.simple_select("get_user_rank","users","group, is_super_admin","uid = ?","") + adapter.SimpleSelect("get_user_rank","users","group, is_super_admin","uid = ?","") - adapter.simple_select("get_user_active","users","active","uid = ?","") + adapter.SimpleSelect("get_user_active","users","active","uid = ?","") - adapter.simple_select("get_user_group","users","group","uid = ?","") + adapter.SimpleSelect("get_user_group","users","group","uid = ?","") - adapter.simple_select("get_emails_by_user","emails","email, validated","uid = ?","") + adapter.SimpleSelect("get_emails_by_user","emails","email, validated","uid = ?","") - adapter.simple_select("get_topic_basic","topics","title, content","tid = ?","") + adapter.SimpleSelect("get_topic_basic","topics","title, content","tid = ?","") - adapter.simple_select("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","") + adapter.SimpleSelect("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","") return nil } -func write_left_joins(adapter DB_Adapter) error { - adapter.simple_left_join("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") +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.simple_left_join("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_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.simple_left_join("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_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.simple_left_join("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_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.simple_left_join("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_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.simple_left_join("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_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 DB_Adapter) error { - adapter.simple_inner_join("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 = ?","") +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 = ?","") return nil } -func write_inserts(adapter DB_Adapter) error { - adapter.simple_insert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,ipaddress,words,createdBy","?,?,?,?,NOW(),NOW(),?,?,?") +func write_inserts(adapter qgen.DB_Adapter) error { + adapter.SimpleInsert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,ipaddress,words,createdBy","?,?,?,?,NOW(),NOW(),?,?,?") - adapter.simple_insert("create_report","topics","title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class","?,?,?,NOW(),NOW(),?,?,1,'report'") + adapter.SimpleInsert("create_report","topics","title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class","?,?,?,NOW(),NOW(),?,?,1,'report'") - adapter.simple_insert("create_reply","replies","tid,content,parsed_content,createdAt,ipaddress,words,createdBy","?,?,?,NOW(),?,?,?") + adapter.SimpleInsert("create_reply","replies","tid,content,parsed_content,createdAt,ipaddress,words,createdBy","?,?,?,NOW(),?,?,?") - adapter.simple_insert("create_action_reply","replies","tid,actionType,ipaddress,createdBy","?,?,?,?") + adapter.SimpleInsert("create_action_reply","replies","tid,actionType,ipaddress,createdBy","?,?,?,?") - adapter.simple_insert("create_like","likes","weight, targetItem, targetType, sentBy","?,?,?,?") + adapter.SimpleInsert("create_like","likes","weight, targetItem, targetType, sentBy","?,?,?,?") - adapter.simple_insert("add_activity","activity_stream","actor,targetUser,event,elementType,elementID","?,?,?,?,?") + adapter.SimpleInsert("add_activity","activity_stream","actor,targetUser,event,elementType,elementID","?,?,?,?,?") - adapter.simple_insert("notify_one","activity_stream_matches","watcher,asid","?,?") + adapter.SimpleInsert("notify_one","activity_stream_matches","watcher,asid","?,?") // Add an admin version of register_stmt with more flexibility? // create_account_stmt, err = db.Prepare("INSERT INTO - adapter.simple_insert("register","users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,?,?,''") + adapter.SimpleInsert("register","users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,?,?,''") - adapter.simple_insert("add_email","emails","email, uid, validated, token","?,?,?,?") + adapter.SimpleInsert("add_email","emails","email, uid, validated, token","?,?,?,?") - adapter.simple_insert("create_profile_reply","users_replies","uid,content,parsed_content,createdAt,createdBy","?,?,?,NOW(),?") + adapter.SimpleInsert("create_profile_reply","users_replies","uid,content,parsed_content,createdAt,createdBy","?,?,?,NOW(),?") - adapter.simple_insert("add_subscription","activity_subscriptions","user,targetID,targetType,level","?,?,?,2") + adapter.SimpleInsert("add_subscription","activity_subscriptions","user,targetID,targetType,level","?,?,?,2") - adapter.simple_insert("create_forum","forums","name, desc, active, preset","?,?,?,?") + adapter.SimpleInsert("create_forum","forums","name, desc, active, preset","?,?,?,?") - adapter.simple_insert("add_forum_perms_to_forum","forums_permissions","gid,fid,preset,permissions","?,?,?,?") + adapter.SimpleInsert("add_forum_perms_to_forum","forums_permissions","gid,fid,preset,permissions","?,?,?,?") - adapter.simple_insert("add_plugin","plugins","uname,active","?,?") + adapter.SimpleInsert("add_plugin","plugins","uname,active","?,?") - adapter.simple_insert("add_theme","themes","uname,default","?,?") + adapter.SimpleInsert("add_theme","themes","uname,default","?,?") - adapter.simple_insert("create_group","users_groups","name, tag, is_admin, is_mod, is_banned, permissions","?,?,?,?,?,?") + adapter.SimpleInsert("create_group","users_groups","name, tag, is_admin, is_mod, is_banned, permissions","?,?,?,?,?,?") - adapter.simple_insert("add_modlog_entry","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,NOW()") + adapter.SimpleInsert("add_modlog_entry","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,NOW()") - adapter.simple_insert("add_adminlog_entry","administration_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,NOW()") + adapter.SimpleInsert("add_adminlog_entry","administration_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,NOW()") return nil } -func write_replaces(adapter DB_Adapter) error { - adapter.simple_replace("add_forum_perms_to_group","forums_permissions","gid,fid,preset,permissions","?,?,?,?") +func write_replaces(adapter qgen.DB_Adapter) error { + adapter.SimpleReplace("add_forum_perms_to_group","forums_permissions","gid,fid,preset,permissions","?,?,?,?") return nil } -func write_updates(adapter DB_Adapter) error { - adapter.simple_update("add_replies_to_topic","topics","postCount = postCount + ?, lastReplyAt = NOW()","tid = ?") +func write_updates(adapter qgen.DB_Adapter) error { + adapter.SimpleUpdate("add_replies_to_topic","topics","postCount = postCount + ?, lastReplyAt = NOW()","tid = ?") - adapter.simple_update("remove_replies_from_topic","topics","postCount = postCount - ?","tid = ?") + adapter.SimpleUpdate("remove_replies_from_topic","topics","postCount = postCount - ?","tid = ?") - adapter.simple_update("add_topics_to_forum","forums","topicCount = topicCount + ?","fid = ?") + adapter.SimpleUpdate("add_topics_to_forum","forums","topicCount = topicCount + ?","fid = ?") - adapter.simple_update("remove_topics_from_forum","forums","topicCount = topicCount - ?","fid = ?") + adapter.SimpleUpdate("remove_topics_from_forum","forums","topicCount = topicCount - ?","fid = ?") - adapter.simple_update("update_forum_cache","forums","lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = NOW()","fid = ?") + adapter.SimpleUpdate("update_forum_cache","forums","lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = NOW()","fid = ?") - adapter.simple_update("add_likes_to_topic","topics","likeCount = likeCount + ?","tid = ?") + adapter.SimpleUpdate("add_likes_to_topic","topics","likeCount = likeCount + ?","tid = ?") - adapter.simple_update("add_likes_to_reply","replies","likeCount = likeCount + ?","rid = ?") + adapter.SimpleUpdate("add_likes_to_reply","replies","likeCount = likeCount + ?","rid = ?") - adapter.simple_update("edit_topic","topics","title = ?, content = ?, parsed_content = ?, is_closed = ?","tid = ?") + adapter.SimpleUpdate("edit_topic","topics","title = ?, content = ?, parsed_content = ?, is_closed = ?","tid = ?") - adapter.simple_update("edit_reply","replies","content = ?, parsed_content = ?","rid = ?") + adapter.SimpleUpdate("edit_reply","replies","content = ?, parsed_content = ?","rid = ?") - adapter.simple_update("stick_topic","topics","sticky = 1","tid = ?") + adapter.SimpleUpdate("stick_topic","topics","sticky = 1","tid = ?") - adapter.simple_update("unstick_topic","topics","sticky = 0","tid = ?") + adapter.SimpleUpdate("unstick_topic","topics","sticky = 0","tid = ?") - adapter.simple_update("update_last_ip","users","last_ip = ?","uid = ?") + adapter.SimpleUpdate("update_last_ip","users","last_ip = ?","uid = ?") - adapter.simple_update("update_session","users","session = ?","uid = ?") + adapter.SimpleUpdate("update_session","users","session = ?","uid = ?") - adapter.simple_update("logout","users","session = ''","uid = ?") + adapter.SimpleUpdate("logout","users","session = ''","uid = ?") - adapter.simple_update("set_password","users","password = ?, salt = ?","uid = ?") + adapter.SimpleUpdate("set_password","users","password = ?, salt = ?","uid = ?") - adapter.simple_update("set_avatar","users","avatar = ?","uid = ?") + adapter.SimpleUpdate("set_avatar","users","avatar = ?","uid = ?") - adapter.simple_update("set_username","users","name = ?","uid = ?") + adapter.SimpleUpdate("set_username","users","name = ?","uid = ?") - adapter.simple_update("change_group","users","group = ?","uid = ?") + adapter.SimpleUpdate("change_group","users","group = ?","uid = ?") - adapter.simple_update("activate_user","users","active = 1","uid = ?") + adapter.SimpleUpdate("activate_user","users","active = 1","uid = ?") - adapter.simple_update("update_user_level","users","level = ?","uid = ?") + adapter.SimpleUpdate("update_user_level","users","level = ?","uid = ?") - adapter.simple_update("increment_user_score","users","score = score + ?","uid = ?") + adapter.SimpleUpdate("increment_user_score","users","score = score + ?","uid = ?") - adapter.simple_update("increment_user_posts","users","posts = posts + ?","uid = ?") + adapter.SimpleUpdate("increment_user_posts","users","posts = posts + ?","uid = ?") - adapter.simple_update("increment_user_bigposts","users","posts = posts + ?, bigposts = bigposts + ?","uid = ?") + adapter.SimpleUpdate("increment_user_bigposts","users","posts = posts + ?, bigposts = bigposts + ?","uid = ?") - adapter.simple_update("increment_user_megaposts","users","posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?","uid = ?") + adapter.SimpleUpdate("increment_user_megaposts","users","posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?","uid = ?") - adapter.simple_update("increment_user_topics","users","topics = topics + ?","uid = ?") + adapter.SimpleUpdate("increment_user_topics","users","topics = topics + ?","uid = ?") - adapter.simple_update("edit_profile_reply","users_replies","content = ?, parsed_content = ?","rid = ?") + adapter.SimpleUpdate("edit_profile_reply","users_replies","content = ?, parsed_content = ?","rid = ?") //delete_forum_stmt, err = db.Prepare("delete from forums where fid = ?") - adapter.simple_update("delete_forum","forums","name= '', active = 0","fid = ?") + adapter.SimpleUpdate("delete_forum","forums","name= '', active = 0","fid = ?") - adapter.simple_update("update_forum","forums","name = ?, desc = ?, active = ?, preset = ?","fid = ?") + adapter.SimpleUpdate("update_forum","forums","name = ?, desc = ?, active = ?, preset = ?","fid = ?") - adapter.simple_update("update_setting","settings","content = ?","name = ?") + adapter.SimpleUpdate("update_setting","settings","content = ?","name = ?") - adapter.simple_update("update_plugin","plugins","active = ?","uname = ?") + adapter.SimpleUpdate("update_plugin","plugins","active = ?","uname = ?") - adapter.simple_update("update_theme","themes","default = ?","uname = ?") + adapter.SimpleUpdate("update_theme","themes","default = ?","uname = ?") - adapter.simple_update("update_user","users","name = ?, email = ?, group = ?","uid = ?") + adapter.SimpleUpdate("update_user","users","name = ?, email = ?, group = ?","uid = ?") - adapter.simple_update("update_group_perms","users_groups","permissions = ?","gid = ?") + adapter.SimpleUpdate("update_group_perms","users_groups","permissions = ?","gid = ?") - adapter.simple_update("update_group_rank","users_groups","is_admin = ?, is_mod = ?, is_banned = ?","gid = ?") + adapter.SimpleUpdate("update_group_rank","users_groups","is_admin = ?, is_mod = ?, is_banned = ?","gid = ?") - adapter.simple_update("update_group","users_groups","name = ?, tag = ?","gid = ?") + adapter.SimpleUpdate("update_group","users_groups","name = ?, tag = ?","gid = ?") return nil } -func write_deletes(adapter DB_Adapter) error { - adapter.simple_delete("delete_reply","replies","rid = ?") +func write_deletes(adapter qgen.DB_Adapter) error { + adapter.SimpleDelete("delete_reply","replies","rid = ?") - adapter.simple_delete("delete_topic","topics","tid = ?") + adapter.SimpleDelete("delete_topic","topics","tid = ?") - adapter.simple_delete("delete_profile_reply","users_replies","rid = ?") + adapter.SimpleDelete("delete_profile_reply","users_replies","rid = ?") - adapter.simple_delete("delete_forum_perms_by_forum","forums_permissions","fid = ?") + adapter.SimpleDelete("delete_forum_perms_by_forum","forums_permissions","fid = ?") return nil } \ No newline at end of file diff --git a/topic.go b/topic.go index dbdb8ea6..4399e0dd 100644 --- a/topic.go +++ b/topic.go @@ -1,9 +1,7 @@ package main //import "fmt" -import "sync" import "strconv" import "html/template" -import "database/sql" type Topic struct { @@ -88,222 +86,6 @@ type TopicsRow struct 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, &topic.Data) - 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, &topic.Data) - 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, &topic.Data) - 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, &topic.Data) - 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, &topic.Data) - 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, &topic.Data) - 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, &topic.Data) - 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, &topic.Data) - 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) diff --git a/topic_store.go b/topic_store.go new file mode 100644 index 00000000..bf289719 --- /dev/null +++ b/topic_store.go @@ -0,0 +1,223 @@ +package main + +import "sync" +import "database/sql" + +var topics TopicStore + +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 + get *sql.Stmt + sync.RWMutex +} + +func NewStaticTopicStore(capacity int) *StaticTopicStore { + return &StaticTopicStore{items:make(map[int]*Topic),capacity:capacity,get:get_topic_stmt} +} + +func (sts *StaticTopicStore) Get(id int) (*Topic, error) { + sts.RLock() + item, ok := sts.items[id] + sts.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.RLock() + topic, ok := sts.items[id] + sts.RUnlock() + if ok { + return topic, nil + } + + 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 { + sts.Add(topic) + } + return topic, err +} + +func (sts *StaticTopicStore) 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 { + 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 { + sts.Set(topic) + } else { + sts.Remove(id) + } + return err +} + +func (sts *StaticTopicStore) Set(item *Topic) error { + sts.Lock() + _, ok := sts.items[item.ID] + if ok { + sts.items[item.ID] = item + } else if sts.length >= sts.capacity { + sts.Unlock() + return ErrStoreCapacityOverflow + } else { + sts.items[item.ID] = item + sts.length++ + } + sts.Unlock() + return nil +} + +func (sts *StaticTopicStore) Add(item *Topic) error { + if sts.length >= sts.capacity { + return ErrStoreCapacityOverflow + } + sts.Lock() + sts.items[item.ID] = item + sts.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.Lock() + delete(sts.items,id) + sts.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 { + get *sql.Stmt +} + +func NewSqlTopicStore() *SqlTopicStore { + return &SqlTopicStore{get_topic_stmt} +} + +func (sts *SqlTopicStore) Get(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 *SqlTopicStore) GetUnsafe(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 *SqlTopicStore) CascadeGet(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 *SqlTopicStore) 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 *SqlTopicStore) 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) + return err +} + +// Placeholder methods, the actual queries are done elsewhere +func (sts *SqlTopicStore) Set(item *Topic) error { + return nil +} +func (sts *SqlTopicStore) Add(item *Topic) error { + return nil +} +func (sts *SqlTopicStore) AddUnsafe(item *Topic) error { + return nil +} +func (sts *SqlTopicStore) Remove(id int) error { + return nil +} +func (sts *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 (sts *SqlTopicStore) GetLength() int { + return 0 // Return the total number of topics on the forums? +} diff --git a/user.go b/user.go index 725ae873..6c1e14ab 100644 --- a/user.go +++ b/user.go @@ -2,8 +2,6 @@ package main import ( //"fmt" - "sync" - "strings" "strconv" "net" "net/http" @@ -49,285 +47,6 @@ type Email struct Token string } -type UserStore interface { - Load(id int) error - Get(id int) (*User, error) - GetUnsafe(id int) (*User, error) - CascadeGet(id int) (*User, error) - BypassGet(id int) (*User, error) - Set(item *User) error - Add(item *User) error - AddUnsafe(item *User) error - //SetConn(conn interface{}) error - //GetConn() interface{} - Remove(id int) error - RemoveUnsafe(id int) error - GetLength() int - GetCapacity() int -} - -type StaticUserStore struct { - items map[int]*User - length int - capacity int - sync.RWMutex -} - -func NewStaticUserStore(capacity int) *StaticUserStore { - return &StaticUserStore{items:make(map[int]*User),capacity:capacity} -} - -func (sts *StaticUserStore) Get(id int) (*User, error) { - sts.RLock() - item, ok := sts.items[id] - sts.RUnlock() - if ok { - return item, nil - } - return item, sql.ErrNoRows -} - -func (sts *StaticUserStore) GetUnsafe(id int) (*User, error) { - item, ok := sts.items[id] - if ok { - return item, nil - } - return item, sql.ErrNoRows -} - -func (sts *StaticUserStore) CascadeGet(id int) (*User, error) { - sts.RLock() - user, ok := sts.items[id] - sts.RUnlock() - if ok { - return user, nil - } - - user = &User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(user) - if err == nil { - sts.Set(user) - } - return user, err -} - -func (sts *StaticUserStore) BypassGet(id int) (*User, error) { - user := &User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(user) - return user, err -} - -func (sts *StaticUserStore) Load(id int) error { - user := &User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 { - sts.Remove(id) - return err - } - - if user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(user) - sts.Set(user) - return nil -} - -func (sts *StaticUserStore) Set(item *User) error { - sts.Lock() - user, ok := sts.items[item.ID] - if ok { - sts.Unlock() - *user = *item - } else if sts.length >= sts.capacity { - sts.Unlock() - return ErrStoreCapacityOverflow - } else { - sts.items[item.ID] = item - sts.Unlock() - sts.length++ - } - return nil -} - -func (sts *StaticUserStore) Add(item *User) error { - if sts.length >= sts.capacity { - return ErrStoreCapacityOverflow - } - sts.Lock() - sts.items[item.ID] = item - sts.Unlock() - sts.length++ - return nil -} - -func (sts *StaticUserStore) AddUnsafe(item *User) error { - if sts.length >= sts.capacity { - return ErrStoreCapacityOverflow - } - sts.items[item.ID] = item - sts.length++ - return nil -} - -func (sts *StaticUserStore) Remove(id int) error { - sts.Lock() - delete(sts.items,id) - sts.Unlock() - sts.length-- - return nil -} - -func (sts *StaticUserStore) RemoveUnsafe(id int) error { - delete(sts.items,id) - sts.length-- - return nil -} - -func (sts *StaticUserStore) GetLength() int { - return sts.length -} - -func (sts *StaticUserStore) SetCapacity(capacity int) { - sts.capacity = capacity -} - -func (sts *StaticUserStore) GetCapacity() int { - return sts.capacity -} - -//type DynamicUserStore struct { -// items_expiries list.List -// items map[int]*User -//} - -type SqlUserStore struct { -} - -func NewSqlUserStore() *SqlUserStore { - return &SqlUserStore{} -} - -func (sus *SqlUserStore) Get(id int) (*User, error) { - user := User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(&user) - return &user, err -} - -func (sus *SqlUserStore) GetUnsafe(id int) (*User, error) { - user := User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(&user) - return &user, err -} - -func (sus *SqlUserStore) CascadeGet(id int) (*User, error) { - user := User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(&user) - return &user, err -} - -func (sus *SqlUserStore) BypassGet(id int) (*User, error) { - user := User{ID:id,Loggedin:true} - err := get_full_user_stmt.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 user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - user.Tag = groups[user.Group].Tag - init_user_perms(&user) - return &user, err -} - -func (sus *SqlUserStore) Load(id int) error { - user := &User{ID:id} - // Simplify this into a quick check whether the user exists - err := get_full_user_stmt.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) - return err -} - -// Placeholder methods, the actual queries are done elsewhere -func (sus *SqlUserStore) Set(item *User) error { - return nil -} -func (sus *SqlUserStore) Add(item *User) error { - return nil -} -func (sus *SqlUserStore) AddUnsafe(item *User) error { - return nil -} -func (sus *SqlUserStore) Remove(id int) error { - return nil -} -func (sus *SqlUserStore) RemoveUnsafe(id int) error { - return nil -} -func (sus *SqlUserStore) GetCapacity() int { - return 0 -} - -func (sus *SqlUserStore) GetLength() int { - return 0 // Return the total number of users registered on the forums? -} - func SetPassword(uid int, password string) (error) { salt, err := GenerateSafeString(saltLength) if err != nil { diff --git a/user_store.go b/user_store.go new file mode 100644 index 00000000..2c340f77 --- /dev/null +++ b/user_store.go @@ -0,0 +1,287 @@ +package main + +import "sync" +import "strings" +import "strconv" +import "database/sql" + +var users UserStore + +type UserStore interface { + Load(id int) error + Get(id int) (*User, error) + GetUnsafe(id int) (*User, error) + CascadeGet(id int) (*User, error) + BypassGet(id int) (*User, error) + Set(item *User) error + Add(item *User) error + AddUnsafe(item *User) error + //SetConn(conn interface{}) error + //GetConn() interface{} + Remove(id int) error + RemoveUnsafe(id int) error + GetLength() int + GetCapacity() int +} + +type StaticUserStore struct { + items map[int]*User + length int + capacity int + sync.RWMutex +} + +func NewStaticUserStore(capacity int) *StaticUserStore { + return &StaticUserStore{items:make(map[int]*User),capacity:capacity} +} + +func (sts *StaticUserStore) Get(id int) (*User, error) { + sts.RLock() + item, ok := sts.items[id] + sts.RUnlock() + if ok { + return item, nil + } + return item, sql.ErrNoRows +} + +func (sts *StaticUserStore) GetUnsafe(id int) (*User, error) { + item, ok := sts.items[id] + if ok { + return item, nil + } + return item, sql.ErrNoRows +} + +func (sts *StaticUserStore) CascadeGet(id int) (*User, error) { + sts.RLock() + user, ok := sts.items[id] + sts.RUnlock() + if ok { + return user, nil + } + + user = &User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(user) + if err == nil { + sts.Set(user) + } + return user, err +} + +func (sts *StaticUserStore) BypassGet(id int) (*User, error) { + user := &User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(user) + return user, err +} + +func (sts *StaticUserStore) Load(id int) error { + user := &User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 { + sts.Remove(id) + return err + } + + if user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(user) + sts.Set(user) + return nil +} + +func (sts *StaticUserStore) Set(item *User) error { + sts.Lock() + user, ok := sts.items[item.ID] + if ok { + sts.Unlock() + *user = *item + } else if sts.length >= sts.capacity { + sts.Unlock() + return ErrStoreCapacityOverflow + } else { + sts.items[item.ID] = item + sts.Unlock() + sts.length++ + } + return nil +} + +func (sts *StaticUserStore) Add(item *User) error { + if sts.length >= sts.capacity { + return ErrStoreCapacityOverflow + } + sts.Lock() + sts.items[item.ID] = item + sts.Unlock() + sts.length++ + return nil +} + +func (sts *StaticUserStore) AddUnsafe(item *User) error { + if sts.length >= sts.capacity { + return ErrStoreCapacityOverflow + } + sts.items[item.ID] = item + sts.length++ + return nil +} + +func (sts *StaticUserStore) Remove(id int) error { + sts.Lock() + delete(sts.items,id) + sts.Unlock() + sts.length-- + return nil +} + +func (sts *StaticUserStore) RemoveUnsafe(id int) error { + delete(sts.items,id) + sts.length-- + return nil +} + +func (sts *StaticUserStore) GetLength() int { + return sts.length +} + +func (sts *StaticUserStore) SetCapacity(capacity int) { + sts.capacity = capacity +} + +func (sts *StaticUserStore) GetCapacity() int { + return sts.capacity +} + +//type DynamicUserStore struct { +// items_expiries list.List +// items map[int]*User +//} + +type SqlUserStore struct { +} + +func NewSqlUserStore() *SqlUserStore { + return &SqlUserStore{} +} + +func (sus *SqlUserStore) Get(id int) (*User, error) { + user := User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) + return &user, err +} + +func (sus *SqlUserStore) GetUnsafe(id int) (*User, error) { + user := User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) + return &user, err +} + +func (sus *SqlUserStore) CascadeGet(id int) (*User, error) { + user := User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) + return &user, err +} + +func (sus *SqlUserStore) BypassGet(id int) (*User, error) { + user := User{ID:id,Loggedin:true} + err := get_full_user_stmt.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 user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) + return &user, err +} + +func (sus *SqlUserStore) Load(id int) error { + user := &User{ID:id} + // Simplify this into a quick check whether the user exists + err := get_full_user_stmt.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) + return err +} + +// Placeholder methods, the actual queries are done elsewhere +func (sus *SqlUserStore) Set(item *User) error { + return nil +} +func (sus *SqlUserStore) Add(item *User) error { + return nil +} +func (sus *SqlUserStore) AddUnsafe(item *User) error { + return nil +} +func (sus *SqlUserStore) Remove(id int) error { + return nil +} +func (sus *SqlUserStore) RemoveUnsafe(id int) error { + return nil +} +func (sus *SqlUserStore) GetCapacity() int { + return 0 +} + +func (sus *SqlUserStore) GetLength() int { + return 0 // Return the total number of users registered on the forums? +}