From 35ddc89009398ae44884d34af7471eac861cc261 Mon Sep 17 00:00:00 2001 From: Azareal Date: Wed, 1 Jan 2020 07:57:54 +1000 Subject: [PATCH] Optimise the database layer. Refactor database adapters. Experimental last ip cutoff. More parser test cases. --- common/alerts.go | 2 +- common/ip_search.go | 16 +- common/password_reset.go | 25 +- common/report_store.go | 4 +- common/routes_common.go | 18 +- common/site.go | 6 + common/user.go | 11 +- docs/configuration.md | 6 +- extend/plugin_bbcode.go | 228 ++++++------ gen_mssql.go | 12 +- gen_mysql.go | 6 +- gen_pgsql.go | 6 +- parser_test.go | 7 + public/account.js | 2 +- public/analytics.js | 4 +- public/member.js | 2 +- public/panel_forums.js | 10 +- public/panel_menu_items.js | 12 +- public/widgets.js | 37 +- query_gen/accumulator.go | 33 +- query_gen/mssql.go | 256 +++++++------- query_gen/mysql.go | 384 ++++++++++----------- query_gen/pgsql.go | 33 +- query_gen/querygen.go | 20 +- query_gen/utils.go | 306 ++++++++-------- routes.go | 14 +- routes/account.go | 14 +- routes/panel/backups.go | 2 +- routes/panel/forums.go | 13 +- routes/panel/groups.go | 30 +- routes/panel/pages.go | 6 +- routes/panel/plugins.go | 6 +- routes/panel/settings.go | 2 +- routes/panel/themes.go | 16 +- routes/panel/users.go | 6 +- routes/panel/word_filters.go | 8 +- routes/poll.go | 2 +- routes/profile_reply.go | 2 +- routes/reply.go | 4 +- routes/topic.go | 21 +- routes/user.go | 6 +- templates/create_topic.html | 8 +- templates/forum.html | 4 +- templates/forum_gallery.html | 6 +- templates/panel_themes_widgets_widget.html | 4 +- templates/topics.html | 4 +- templates/topics_quick_topic.html | 2 +- tickloop.go | 46 ++- 48 files changed, 877 insertions(+), 795 deletions(-) diff --git a/common/alerts.go b/common/alerts.go index 49ea2c87..fb28bf73 100644 --- a/common/alerts.go +++ b/common/alerts.go @@ -44,7 +44,7 @@ func init() { DbInits.Add(func(acc *qgen.Accumulator) error { alertStmts = AlertStmts{ notifyWatchers: acc.SimpleInsertInnerJoin( - qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""}, + qgen.DBInsert{"activity_stream_matches", "watcher,asid", ""}, qgen.DBJoin{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""}, ), notifyOne: acc.Insert("activity_stream_matches").Columns("watcher,asid").Fields("?,?").Prepare(), diff --git a/common/ip_search.go b/common/ip_search.go index 460367ef..d65498a2 100644 --- a/common/ip_search.go +++ b/common/ip_search.go @@ -23,18 +23,17 @@ type DefaultIPSearcher struct { func NewDefaultIPSearcher() (*DefaultIPSearcher, error) { acc := qgen.NewAcc() return &DefaultIPSearcher{ - searchUsers: acc.Select("users").Columns("uid").Where("last_ip = ?").Prepare(), - searchTopics: acc.Select("users").Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ipaddress = ?")).Prepare(), - searchReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("replies").Columns("createdBy").Where("ipaddress = ?")).Prepare(), - searchUsersReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("users_replies").Columns("createdBy").Where("ipaddress = ?")).Prepare(), + searchUsers: acc.Select("users").Columns("uid").Where("last_ip=? OR last_ip LIKE CONCAT('%-',?)").Prepare(), + searchTopics: acc.Select("users").Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ipaddress=?")).Prepare(), + searchReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("replies").Columns("createdBy").Where("ipaddress=?")).Prepare(), + searchUsersReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("users_replies").Columns("createdBy").Where("ipaddress=?")).Prepare(), }, acc.FirstError() } func (s *DefaultIPSearcher) Lookup(ip string) (uids []int, err error) { var uid int reqUserList := make(map[int]bool) - runQuery := func(stmt *sql.Stmt) error { - rows, err := stmt.Query(ip) + runQuery2 := func(rows *sql.Rows, err error) error { if err != nil { return err } @@ -49,8 +48,11 @@ func (s *DefaultIPSearcher) Lookup(ip string) (uids []int, err error) { } return rows.Err() } + runQuery := func(stmt *sql.Stmt) error { + return runQuery2(stmt.Query(ip)) + } - err = runQuery(s.searchUsers) + err = runQuery2(s.searchUsers.Query(ip, ip)) if err != nil { return uids, err } diff --git a/common/password_reset.go b/common/password_reset.go index 7697cb01..354b1073 100644 --- a/common/password_reset.go +++ b/common/password_reset.go @@ -17,11 +17,24 @@ type DefaultPasswordResetter struct { delete *sql.Stmt } +/* + type PasswordReset struct { + Email string `q:"email"` + Uid int `q:"uid"` + Validated bool `q:"validated"` + Token string `q:"token"` + CreatedAt time.Time `q:"createdAt"` + } +*/ + func NewDefaultPasswordResetter(acc *qgen.Accumulator) (*DefaultPasswordResetter, error) { + pr := "password_resets" return &DefaultPasswordResetter{ - getTokens: acc.Select("password_resets").Columns("token").Where("uid = ?").Prepare(), - create: acc.Insert("password_resets").Columns("email, uid, validated, token, createdAt").Fields("?,?,0,?,UTC_TIMESTAMP()").Prepare(), - delete: acc.Delete("password_resets").Where("uid =?").Prepare(), + getTokens: acc.Select(pr).Columns("token").Where("uid = ?").Prepare(), + create: acc.Insert(pr).Columns("email, uid, validated, token, createdAt").Fields("?,?,0,?,UTC_TIMESTAMP()").Prepare(), + //create: acc.Insert(pr).Cols("email,uid,validated=0,token,createdAt=UTC_TIMESTAMP()").Prep(), + delete: acc.Delete(pr).Where("uid=?").Prepare(), + //model: acc.Model(w).Cols("email,uid,validated=0,token").Key("uid").CreatedAt("createdAt").Prep(), }, acc.FirstError() } @@ -45,16 +58,14 @@ func (r *DefaultPasswordResetter) ValidateToken(uid int, token string) error { success := false for rows.Next() { var rtoken string - err := rows.Scan(&rtoken) - if err != nil { + if err := rows.Scan(&rtoken); err != nil { return err } if subtle.ConstantTimeCompare([]byte(token), []byte(rtoken)) == 1 { success = true } } - err = rows.Err() - if err != nil { + if err = rows.Err(); err != nil { return err } diff --git a/common/report_store.go b/common/report_store.go index 3a21f7cd..03be7313 100644 --- a/common/report_store.go +++ b/common/report_store.go @@ -44,7 +44,7 @@ func (s *DefaultReportStore) Create(title string, content string, u *User, itemT return 0, ErrAlreadyReported } - res, err := s.create.Exec(title, content, ParseMessage(content, 0, "", nil), u.LastIP, u.ID, u.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID) + res, err := s.create.Exec(title, content, ParseMessage(content, 0, "", nil), u.GetIP(), u.ID, u.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID) if err != nil { return 0, err } @@ -53,6 +53,6 @@ func (s *DefaultReportStore) Create(title string, content string, u *User, itemT return 0, err } tid = int(lastID) - + return tid, Forums.AddTopic(tid, u.ID, ReportForumID) } diff --git a/common/routes_common.go b/common/routes_common.go index 27003863..876c7f68 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -316,15 +316,15 @@ func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) { } } - usercpy.LastIP = host - - if usercpy.Loggedin && host != usercpy.LastIP { - err = usercpy.UpdateIP(host) + if usercpy.Loggedin && host != usercpy.GetIP() { + mon := time.Now().Month() + err = usercpy.UpdateIP(strconv.Itoa(int(mon)) + "-" + host) if err != nil { InternalError(err, w, r) return *usercpy, false } } + usercpy.LastIP = host return *usercpy, true } @@ -350,11 +350,11 @@ func UploadAvatar(w http.ResponseWriter, r *http.Request, user User, tuid int) ( if hdr.Filename == "" { continue } - infile, err := hdr.Open() + inFile, err := hdr.Open() if err != nil { return "", LocalError("Upload failed", w, r, user) } - defer infile.Close() + defer inFile.Close() if ext == "" { extarr := strings.Split(hdr.Filename, ".") @@ -377,13 +377,13 @@ func UploadAvatar(w http.ResponseWriter, r *http.Request, user User, tuid int) ( } // TODO: Centralise this string, so we don't have to change it in two different places when it changes - outfile, err := os.Create("./uploads/avatar_" + strconv.Itoa(tuid) + "." + ext) + outFile, err := os.Create("./uploads/avatar_" + strconv.Itoa(tuid) + "." + ext) if err != nil { return "", LocalError("Upload failed [File Creation Failed]", w, r, user) } - defer outfile.Close() + defer outFile.Close() - _, err = io.Copy(outfile, infile) + _, err = io.Copy(outFile, inFile) if err != nil { return "", LocalError("Upload failed [Copy Failed]", w, r, user) } diff --git a/common/site.go b/common/site.go index d840c3f6..fc7ff49f 100644 --- a/common/site.go +++ b/common/site.go @@ -222,6 +222,12 @@ func ProcessConfig() (err error) { if Config.LogPruneCutoff == 0 { Config.LogPruneCutoff = 180 // Default cutoff } + if Config.LastIPCutoff == 0 { + Config.LastIPCutoff = 3 // Default cutoff + } + if Config.LastIPCutoff > 12 { + Config.LastIPCutoff = 12 + } if Config.NoEmbed { DefaultParseSettings.NoEmbed = true } diff --git a/common/user.go b/common/user.go index e03081c3..34f8f266 100644 --- a/common/user.go +++ b/common/user.go @@ -201,7 +201,7 @@ func (u *User) Unban() error { } func (u *User) deleteScheduleGroupTx(tx *sql.Tx) error { - deleteScheduleGroupStmt, err := qgen.Builder.SimpleDeleteTx(tx, "users_groups_scheduler", "uid = ?") + deleteScheduleGroupStmt, err := qgen.Builder.SimpleDeleteTx(tx, "users_groups_scheduler", "uid=?") if err != nil { return err } @@ -338,9 +338,14 @@ func (u *User) ChangeGroup(group int) (err error) { return u.bindStmt(userStmts.changeGroup, group) } +func (u *User) GetIP() string { + spl := strings.Split(u.LastIP,"-") + return spl[len(spl)-1] +} + // ! Only updates the database not the *User for safety reasons -func (u *User) UpdateIP(host string) error { - _, err := userStmts.updateLastIP.Exec(host, u.ID) +func (u *User) UpdateIP(ip string) error { + _, err := userStmts.updateLastIP.Exec(ip, u.ID) if uc := Users.GetCache(); uc != nil { uc.Remove(u.ID) } diff --git a/docs/configuration.md b/docs/configuration.md index 8fab52b8..c747af9e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -84,9 +84,11 @@ BuildSlugs - Whether you want the title appear in the URL. For instance: `/topic ServerCount - The number of instances you're running. This setting is currently experimental. -PostIPCutoff - The number of days which need to pass before the IP data for a post is automatically deleted. 0 defaults to whatever the current default is, currently 180 and -1 disables this feature. Default: 0 +LastIPCutoff - The number of months which need to pass before the last IP stored for a user is automatically deleted. Capped at 12. 0 defaults to whatever the current default is, currently 3 and -1 disables this feature. -LogPruneCutoff - The number of days which need to pass before the login and registration logs are pruned. 0 defaults to whatever the current default is, currently 365 and -1 disables this feature. Default: 0 +PostIPCutoff - The number of days which need to pass before the IP data for a post is automatically deleted. 0 defaults to whatever the current default is, currently 120 and -1 disables this feature. Default: 0 + +LogPruneCutoff - The number of days which need to pass before the login and registration logs are pruned. 0 defaults to whatever the current default is, currently 180 and -1 disables this feature. Default: 0 DisableLiveTopicList - This switch allows you to disable the live topic list. Default: false diff --git a/extend/plugin_bbcode.go b/extend/plugin_bbcode.go index 55a8008c..66a4433c 100644 --- a/extend/plugin_bbcode.go +++ b/extend/plugin_bbcode.go @@ -74,24 +74,25 @@ func BbcodeRegexParse(msg string) string { // Only does the simple BBCode like [u], [b], [i] and [s] func bbcodeSimpleParse(msg string) string { var hasU, hasB, hasI, hasS bool - msgbytes := []byte(msg) - for i := 0; (i + 2) < len(msgbytes); i++ { - if msgbytes[i] == '[' && msgbytes[i+2] == ']' { - if msgbytes[i+1] == 'b' && !hasB { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + mbytes := []byte(msg) + for i := 0; (i + 2) < len(mbytes); i++ { + if mbytes[i] == '[' && mbytes[i+2] == ']' { + ch := mbytes[i+1] + if ch == 'b' && !hasB { + mbytes[i] = '<' + mbytes[i+2] = '>' hasB = true - } else if msgbytes[i+1] == 'i' && !hasI { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 'i' && !hasI { + mbytes[i] = '<' + mbytes[i+2] = '>' hasI = true - } else if msgbytes[i+1] == 'u' && !hasU { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 'u' && !hasU { + mbytes[i] = '<' + mbytes[i+2] = '>' hasU = true - } else if msgbytes[i+1] == 's' && !hasS { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 's' && !hasS { + mbytes[i] = '<' + mbytes[i+2] = '>' hasS = true } i += 2 @@ -105,46 +106,47 @@ func bbcodeSimpleParse(msg string) string { closeBold := []byte("") closeStrike := []byte("") if hasI { - msgbytes = append(msgbytes, closeItalic...) + mbytes = append(mbytes, closeItalic...) } if hasU { - msgbytes = append(msgbytes, closeUnder...) + mbytes = append(mbytes, closeUnder...) } if hasB { - msgbytes = append(msgbytes, closeBold...) + mbytes = append(mbytes, closeBold...) } if hasS { - msgbytes = append(msgbytes, closeStrike...) + mbytes = append(mbytes, closeStrike...) } } - return string(msgbytes) + return string(mbytes) } // Here for benchmarking purposes. Might add a plugin setting for disabling [code] as it has it's paws everywhere func BbcodeParseWithoutCode(msg string) string { var hasU, hasB, hasI, hasS bool var complexBbc bool - msgbytes := []byte(msg) - for i := 0; (i + 3) < len(msgbytes); i++ { - if msgbytes[i] == '[' { - if msgbytes[i+2] != ']' { - if msgbytes[i+1] == '/' { - if msgbytes[i+3] == ']' { - if msgbytes[i+2] == 'b' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + mbytes := []byte(msg) + for i := 0; (i + 3) < len(mbytes); i++ { + if mbytes[i] == '[' { + if mbytes[i+2] != ']' { + if mbytes[i+1] == '/' { + if mbytes[i+3] == ']' { + switch mbytes[i+2] { + case 'b': + mbytes[i] = '<' + mbytes[i+3] = '>' hasB = false - } else if msgbytes[i+2] == 'i' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + case 'i': + mbytes[i] = '<' + mbytes[i+3] = '>' hasI = false - } else if msgbytes[i+2] == 'u' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + case 'u': + mbytes[i] = '<' + mbytes[i+3] = '>' hasU = false - } else if msgbytes[i+2] == 's' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + case 's': + mbytes[i] = '<' + mbytes[i+3] = '>' hasS = false } i += 3 @@ -155,21 +157,22 @@ func BbcodeParseWithoutCode(msg string) string { complexBbc = true } } else { - if msgbytes[i+1] == 'b' && !hasB { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + ch := mbytes[i+1] + if ch == 'b' && !hasB { + mbytes[i] = '<' + mbytes[i+2] = '>' hasB = true - } else if msgbytes[i+1] == 'i' && !hasI { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 'i' && !hasI { + mbytes[i] = '<' + mbytes[i+2] = '>' hasI = true - } else if msgbytes[i+1] == 'u' && !hasU { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 'u' && !hasU { + mbytes[i] = '<' + mbytes[i+2] = '>' hasU = true - } else if msgbytes[i+1] == 's' && !hasS { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 's' && !hasS { + mbytes[i] = '<' + mbytes[i+2] = '>' hasS = true } i += 2 @@ -184,29 +187,29 @@ func BbcodeParseWithoutCode(msg string) string { closeBold := []byte("") closeStrike := []byte("") if hasI { - msgbytes = append(bytes.TrimSpace(msgbytes), closeItalic...) + mbytes = append(bytes.TrimSpace(mbytes), closeItalic...) } if hasU { - msgbytes = append(bytes.TrimSpace(msgbytes), closeUnder...) + mbytes = append(bytes.TrimSpace(mbytes), closeUnder...) } if hasB { - msgbytes = append(bytes.TrimSpace(msgbytes), closeBold...) + mbytes = append(bytes.TrimSpace(mbytes), closeBold...) } if hasS { - msgbytes = append(bytes.TrimSpace(msgbytes), closeStrike...) + mbytes = append(bytes.TrimSpace(mbytes), closeStrike...) } } // Copy the new complex parser over once the rough edges have been smoothed over if complexBbc { - msg = string(msgbytes) + msg = string(mbytes) msg = bbcodeURL.ReplaceAllString(msg, "$1$2//$3") msg = bbcodeURLLabel.ReplaceAllString(msg, "$4") msg = bbcodeSpoiler.ReplaceAllString(msg, "$1") msg = bbcodeQuotes.ReplaceAllString(msg, "
$1
") return bbcodeCode.ReplaceAllString(msg, "$1") } - return string(msgbytes) + return string(mbytes) } // Does every type of BBCode @@ -214,63 +217,66 @@ func BbcodeFullParse(msg string) string { var hasU, hasB, hasI, hasS, hasC bool var complexBbc bool - msgbytes := []byte(msg) - msgbytes = append(msgbytes, c.SpaceGap...) - for i := 0; i < len(msgbytes); i++ { - if msgbytes[i] == '[' { - if msgbytes[i+2] != ']' { - if msgbytes[i+1] == '/' { - if msgbytes[i+3] == ']' { + mbytes := []byte(msg) + mbytes = append(mbytes, c.SpaceGap...) + for i := 0; i < len(mbytes); i++ { + if mbytes[i] == '[' { + if mbytes[i+2] != ']' { + if mbytes[i+1] == '/' { + if mbytes[i+3] == ']' { if !hasC { - if msgbytes[i+2] == 'b' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + switch mbytes[i+2] { + case 'b': + mbytes[i] = '<' + mbytes[i+3] = '>' hasB = false - } else if msgbytes[i+2] == 'i' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + case 'i': + mbytes[i] = '<' + mbytes[i+3] = '>' hasI = false - } else if msgbytes[i+2] == 'u' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + case 'u': + mbytes[i] = '<' + mbytes[i+3] = '>' hasU = false - } else if msgbytes[i+2] == 's' { - msgbytes[i] = '<' - msgbytes[i+3] = '>' + case 's': + mbytes[i] = '<' + mbytes[i+3] = '>' hasS = false } i += 3 } } else { - if msgbytes[i+2] == 'c' && msgbytes[i+3] == 'o' && msgbytes[i+4] == 'd' && msgbytes[i+5] == 'e' && msgbytes[i+6] == ']' { + if mbytes[i+6] == ']' && mbytes[i+2] == 'c' && mbytes[i+3] == 'o' && mbytes[i+4] == 'd' && mbytes[i+5] == 'e' { hasC = false i += 7 } complexBbc = true } } else { - if msgbytes[i+1] == 'c' && msgbytes[i+2] == 'o' && msgbytes[i+3] == 'd' && msgbytes[i+4] == 'e' && msgbytes[i+5] == ']' { + // Put the biggest index first to avoid unnecessary bounds checks + if mbytes[i+5] == ']' && mbytes[i+1] == 'c' && mbytes[i+2] == 'o' && mbytes[i+3] == 'd' && mbytes[i+4] == 'e' { hasC = true i += 6 } complexBbc = true } } else if !hasC { - if msgbytes[i+1] == 'b' && !hasB { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + ch := mbytes[i+1] + if ch == 'b' && !hasB { + mbytes[i] = '<' + mbytes[i+2] = '>' hasB = true - } else if msgbytes[i+1] == 'i' && !hasI { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 'i' && !hasI { + mbytes[i] = '<' + mbytes[i+2] = '>' hasI = true - } else if msgbytes[i+1] == 'u' && !hasU { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 'u' && !hasU { + mbytes[i] = '<' + mbytes[i+2] = '>' hasU = true - } else if msgbytes[i+1] == 's' && !hasS { - msgbytes[i] = '<' - msgbytes[i+2] = '>' + } else if ch == 's' && !hasS { + mbytes[i] = '<' + mbytes[i+2] = '>' hasS = true } i += 2 @@ -285,46 +291,46 @@ func BbcodeFullParse(msg string) string { closeBold := []byte("") closeStrike := []byte("") if hasI { - msgbytes = append(bytes.TrimSpace(msgbytes), closeItalic...) + mbytes = append(bytes.TrimSpace(mbytes), closeItalic...) } if hasU { - msgbytes = append(bytes.TrimSpace(msgbytes), closeUnder...) + mbytes = append(bytes.TrimSpace(mbytes), closeUnder...) } if hasB { - msgbytes = append(bytes.TrimSpace(msgbytes), closeBold...) + mbytes = append(bytes.TrimSpace(mbytes), closeBold...) } if hasS { - msgbytes = append(bytes.TrimSpace(msgbytes), closeStrike...) + mbytes = append(bytes.TrimSpace(mbytes), closeStrike...) } - msgbytes = append(msgbytes, c.SpaceGap...) + mbytes = append(mbytes, c.SpaceGap...) } if complexBbc { i := 0 var start, lastTag int var outbytes []byte - for ; i < len(msgbytes); i++ { - if msgbytes[i] == '[' { - if msgbytes[i+1] == 'u' { - if msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' { - i, start, lastTag, outbytes = bbcodeParseURL(i, start, lastTag, msgbytes, outbytes) + for ; i < len(mbytes); i++ { + if mbytes[i] == '[' { + if mbytes[i+1] == 'u' { + if mbytes[i+4] == ']' && mbytes[i+2] == 'r' && mbytes[i+3] == 'l' { + i, start, lastTag, outbytes = bbcodeParseURL(i, start, lastTag, mbytes, outbytes) continue } - } else if msgbytes[i+1] == 'r' { - if bytes.Equal(msgbytes[i+2:i+6], []byte("and]")) { - i, start, lastTag, outbytes = bbcodeParseRand(i, start, lastTag, msgbytes, outbytes) + } else if mbytes[i+1] == 'r' { + if bytes.Equal(mbytes[i+2:i+6], []byte("and]")) { + i, start, lastTag, outbytes = bbcodeParseRand(i, start, lastTag, mbytes, outbytes) } } } } if lastTag != i { - outbytes = append(outbytes, msgbytes[lastTag:]...) + outbytes = append(outbytes, mbytes[lastTag:]...) } if len(outbytes) != 0 { msg = string(outbytes[0 : len(outbytes)-10]) } else { - msg = string(msgbytes[0 : len(msgbytes)-10]) + msg = string(mbytes[0 : len(mbytes)-10]) } // TODO: Optimise these @@ -335,27 +341,27 @@ func BbcodeFullParse(msg string) string { msg = bbcodeSpoiler.ReplaceAllString(msg, "$1") msg = bbcodeH1.ReplaceAllString(msg, "

$1

") } else { - msg = string(msgbytes[0 : len(msgbytes)-10]) + msg = string(mbytes[0 : len(mbytes)-10]) } return msg } // TODO: Strip the containing [url] so the media parser can work it's magic instead? Or do we want to allow something like [url=]label[/url] here? -func bbcodeParseURL(i int, start int, lastTag int, msgbytes []byte, outbytes []byte) (int, int, int, []byte) { +func bbcodeParseURL(i int, start int, lastTag int, mbytes []byte, outbytes []byte) (int, int, int, []byte) { start = i + 5 - outbytes = append(outbytes, msgbytes[lastTag:i]...) + outbytes = append(outbytes, mbytes[lastTag:i]...) i = start - i += c.PartialURLStringLen2(string(msgbytes[start:])) - if !bytes.Equal(msgbytes[i:i+6], []byte("[/url]")) { + i += c.PartialURLStringLen2(string(mbytes[start:])) + if !bytes.Equal(mbytes[i:i+6], []byte("[/url]")) { outbytes = append(outbytes, c.InvalidURL...) return i, start, lastTag, outbytes } outbytes = append(outbytes, c.URLOpen...) - outbytes = append(outbytes, msgbytes[start:i]...) + outbytes = append(outbytes, mbytes[start:i]...) outbytes = append(outbytes, c.URLOpen2...) - outbytes = append(outbytes, msgbytes[start:i]...) + outbytes = append(outbytes, mbytes[start:i]...) outbytes = append(outbytes, c.URLClose...) i += 6 lastTag = i diff --git a/gen_mssql.go b/gen_mssql.go index 9a35023b..e117bef3 100644 --- a/gen_mssql.go +++ b/gen_mssql.go @@ -61,26 +61,26 @@ func _gen_mssql() (err error) { } common.DebugLog("Preparing updateEmail statement.") - stmts.updateEmail, err = db.Prepare("UPDATE [emails] SET [email] = ?,[uid] = ?,[validated] = ?,[token] = ? WHERE [email] = ?") + stmts.updateEmail, err = db.Prepare("UPDATE [emails] SET [email]= ?,[uid]= ?,[validated]= ?,[token]= ? WHERE [email] = ?") if err != nil { log.Print("Error in updateEmail statement.") - log.Print("Bad Query: ","UPDATE [emails] SET [email] = ?,[uid] = ?,[validated] = ?,[token] = ? WHERE [email] = ?") + log.Print("Bad Query: ","UPDATE [emails] SET [email]= ?,[uid]= ?,[validated]= ?,[token]= ? WHERE [email] = ?") return err } common.DebugLog("Preparing setTempGroup statement.") - stmts.setTempGroup, err = db.Prepare("UPDATE [users] SET [temp_group] = ? WHERE [uid] = ?") + stmts.setTempGroup, err = db.Prepare("UPDATE [users] SET [temp_group]= ? WHERE [uid] = ?") if err != nil { log.Print("Error in setTempGroup statement.") - log.Print("Bad Query: ","UPDATE [users] SET [temp_group] = ? WHERE [uid] = ?") + log.Print("Bad Query: ","UPDATE [users] SET [temp_group]= ? WHERE [uid] = ?") return err } common.DebugLog("Preparing bumpSync statement.") - stmts.bumpSync, err = db.Prepare("UPDATE [sync] SET [last_update] = GETUTCDATE()") + stmts.bumpSync, err = db.Prepare("UPDATE [sync] SET [last_update]= GETUTCDATE()") if err != nil { log.Print("Error in bumpSync statement.") - log.Print("Bad Query: ","UPDATE [sync] SET [last_update] = GETUTCDATE()") + log.Print("Bad Query: ","UPDATE [sync] SET [last_update]= GETUTCDATE()") return err } diff --git a/gen_mysql.go b/gen_mysql.go index 6a567026..f54c7386 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -59,21 +59,21 @@ func _gen_mysql() (err error) { } common.DebugLog("Preparing updateEmail statement.") - stmts.updateEmail, err = db.Prepare("UPDATE `emails` SET `email` = ?,`uid` = ?,`validated` = ?,`token` = ? WHERE `email` = ?") + stmts.updateEmail, err = db.Prepare("UPDATE `emails` SET `email`= ?,`uid`= ?,`validated`= ?,`token`= ? WHERE `email` = ?") if err != nil { log.Print("Error in updateEmail statement.") return err } common.DebugLog("Preparing setTempGroup statement.") - stmts.setTempGroup, err = db.Prepare("UPDATE `users` SET `temp_group` = ? WHERE `uid` = ?") + stmts.setTempGroup, err = db.Prepare("UPDATE `users` SET `temp_group`= ? WHERE `uid` = ?") if err != nil { log.Print("Error in setTempGroup statement.") return err } common.DebugLog("Preparing bumpSync statement.") - stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = UTC_TIMESTAMP()") + stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update`= UTC_TIMESTAMP()") if err != nil { log.Print("Error in bumpSync statement.") return err diff --git a/gen_pgsql.go b/gen_pgsql.go index bd0afdb9..34ac47b6 100644 --- a/gen_pgsql.go +++ b/gen_pgsql.go @@ -32,21 +32,21 @@ func _gen_pgsql() (err error) { } common.DebugLog("Preparing updateEmail statement.") - stmts.updateEmail, err = db.Prepare("UPDATE \"emails\" SET `email` = ?,`uid` = ?,`validated` = ?,`token` = ? WHERE `email` = ?") + stmts.updateEmail, err = db.Prepare("UPDATE \"emails\" SET `email`= ?,`uid`= ?,`validated`= ?,`token`= ? WHERE `email` = ?") if err != nil { log.Print("Error in updateEmail statement.") return err } common.DebugLog("Preparing setTempGroup statement.") - stmts.setTempGroup, err = db.Prepare("UPDATE \"users\" SET `temp_group` = ? WHERE `uid` = ?") + stmts.setTempGroup, err = db.Prepare("UPDATE \"users\" SET `temp_group`= ? WHERE `uid` = ?") if err != nil { log.Print("Error in setTempGroup statement.") return err } common.DebugLog("Preparing bumpSync statement.") - stmts.bumpSync, err = db.Prepare("UPDATE \"sync\" SET `last_update` = LOCALTIMESTAMP()") + stmts.bumpSync, err = db.Prepare("UPDATE \"sync\" SET `last_update`= LOCALTIMESTAMP()") if err != nil { log.Print("Error in bumpSync statement.") return err diff --git a/parser_test.go b/parser_test.go index 7c566fee..fc7c2c70 100644 --- a/parser_test.go +++ b/parser_test.go @@ -167,7 +167,14 @@ func TestParser(t *testing.T) { l.Add("// t", "// t") l.Add("http:// t", "[Invalid URL] t") + l.Add("h", "h") + l.Add("ht", "ht") + l.Add("htt", "htt") + l.Add("http", "http") l.Add("http:", "http:") + //t l.Add("http:/", "http:/") + //t l.Add("http:/d", "http:/d") + l.Add("http:d", "http:d") l.Add("https:", "https:") l.Add("ftp:", "ftp:") l.Add("git:", "git:") diff --git a/public/account.js b/public/account.js index ad17da14..93e92090 100644 --- a/public/account.js +++ b/public/account.js @@ -2,7 +2,7 @@ (() => { addInitHook("end_init", () => { - $("#dash_username input").click(function(){ + $("#dash_username input").click(()=>{ $("#dash_username button").show(); }); }); diff --git a/public/analytics.js b/public/analytics.js index 88a1605d..c7cc5646 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -97,7 +97,7 @@ function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, bytes = continue; } let date = new Date(rawLabels[i]*1000); - console.log("date: ", date); + console.log("date:", date); let minutes = "0" + date.getMinutes(); let label = date.getHours() + ":" + minutes.substr(-2); console.log("label:", label); @@ -106,7 +106,7 @@ function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, bytes = } else { for(const i in rawLabels) { let date = new Date(rawLabels[i]*1000); - console.log("date: ", date); + console.log("date:", date); let minutes = "0" + date.getMinutes(); let label = date.getHours() + ":" + minutes.substr(-2); console.log("label:", label); diff --git a/public/member.js b/public/member.js index 4172c54b..baec6626 100644 --- a/public/member.js +++ b/public/member.js @@ -284,7 +284,7 @@ var imageExts = ["png", "jpg", "jpe","jpeg","jif","jfi","jfif", "svg", "bmp", "g function addPollInput() { console.log("clicked on pollinputinput"); let dataPollInput = $(this).parent().attr("data-pollinput"); - console.log("dataPollInput: ", dataPollInput); + console.log("dataPollInput:", dataPollInput); if(dataPollInput == undefined) return; if(dataPollInput != (pollInputIndex-1)) return; $(".poll_content_row .formitem").append(Template_topic_c_poll_input({ diff --git a/public/panel_forums.js b/public/panel_forums.js index 1dda3e75..80d16ec4 100644 --- a/public/panel_forums.js +++ b/public/panel_forums.js @@ -13,14 +13,14 @@ console.log("forums:",forums); Sortable.create(document.getElementById("panel_forums"), { sort: true, onEnd: (evt) => { - console.log("pre forums: ", forums) - console.log("evt: ", evt) + console.log("pre forums:", forums) + console.log("evt:", evt) let oldFid = forums[evt.newIndex]; forums[evt.oldIndex] = oldFid; let newFid = evt.item.getAttribute("data-fid"); - console.log("newFid: ", newFid); + console.log("newFid:", newFid); forums[evt.newIndex] = newFid; - console.log("post forums: ", forums); + console.log("post forums:", forums); } }); @@ -37,7 +37,7 @@ document.getElementById("panel_forums_order_button").addEventListener("click", ( if(req.status!==200) return; let resp = JSON.parse(req.responseText); - console.log("resp: ", resp); + console.log("resp:", resp); // TODO: Should we move other notices into TmplPhrases like this one? pushNotice(phraseBox["panel"]["panel.forums_order_updated"]); if(resp.success==1) return; diff --git a/public/panel_menu_items.js b/public/panel_menu_items.js index e6387760..e671a749 100644 --- a/public/panel_menu_items.js +++ b/public/panel_menu_items.js @@ -9,14 +9,14 @@ for(let i = 0; item = items[i]; i++) menuItems[i] = item.getAttribute("data-miid Sortable.create(document.getElementById("panel_menu_item_holder"), { sort: true, onEnd: (evt) => { - console.log("pre menuItems: ", menuItems) - console.log("evt: ", evt) + console.log("pre menuItems:", menuItems) + console.log("evt:", evt) let oldMiid = menuItems[evt.newIndex]; menuItems[evt.oldIndex] = oldMiid; let newMiid = evt.item.getAttribute("data-miid"); - console.log("newMiid: ", newMiid); + console.log("newMiid:", newMiid); menuItems[evt.newIndex] = newMiid; - console.log("post menuItems: ", menuItems); + console.log("post menuItems:", menuItems); } }); @@ -32,13 +32,13 @@ document.getElementById("panel_menu_items_order_button").addEventListener("click // TODO: Signal the error with a notice if(req.status===200) { let resp = JSON.parse(req.responseText); - console.log("resp: ", resp); + console.log("resp:", resp); // TODO: Should we move other notices into TmplPhrases like this one? pushNotice(phraseBox["panel"]["panel.themes_menus_items_order_updated"]); if(resp.success==1) return; } } catch(ex) { - console.error("exception: ", ex) + console.error("exception:", ex) } console.trace(); } diff --git a/public/widgets.js b/public/widgets.js index 05e6117d..cf7ae932 100644 --- a/public/widgets.js +++ b/public/widgets.js @@ -1,23 +1,23 @@ "use strict"; $(document).ready(() => { - let clickHandle = function(event){ + let clickHandle = function(ev){ console.log("in clickHandle") - event.preventDefault(); - let eparent = $(this).closest(".editable_parent"); - eparent.find(".hide_on_block_edit").addClass("edit_opened"); - eparent.find(".show_on_block_edit").addClass("edit_opened"); - eparent.addClass("in_edit"); + ev.preventDefault(); + let ep = $(this).closest(".editable_parent"); + ep.find(".hide_on_block_edit").addClass("edit_opened"); + ep.find(".show_on_block_edit").addClass("edit_opened"); + ep.addClass("in_edit"); - eparent.find(".widget_save").click(() => { - eparent.find(".hide_on_block_edit").removeClass("edit_opened"); - eparent.find(".show_on_block_edit").removeClass("edit_opened"); - eparent.removeClass("in_edit"); + ep.find(".widget_save").click(() => { + ep.find(".hide_on_block_edit").removeClass("edit_opened"); + ep.find(".show_on_block_edit").removeClass("edit_opened"); + ep.removeClass("in_edit"); }); - eparent.find(".widget_delete").click(function(event) { - event.preventDefault(); - eparent.remove(); + ep.find(".widget_delete").click(function(ev) { + ev.preventDefault(); + ep.remove(); let formData = new URLSearchParams(); formData.append("s",me.User.S); let req = new XMLHttpRequest(); @@ -29,15 +29,14 @@ $(document).ready(() => { $(".widget_item a").click(clickHandle); - let changeHandle = function(event){ + let changeHandle = function(ev){ let wtype = this.options[this.selectedIndex].value; let typeBlock = this.closest(".widget_edit").querySelector(".wtypes"); typeBlock.className = "wtypes wtype_"+wtype; }; - $(".wtype_sel").change(changeHandle); - $(".widget_new a").click(function(event){ + $(".widget_new a").click(function(ev){ console.log("clicked widget_new a") let widgetList = this.closest(".panel_widgets"); let widgetNew = this.closest(".widget_new"); @@ -51,10 +50,10 @@ $(document).ready(() => { $(".wtype_sel").change(changeHandle); }); - $(".widget_save").click(function(event){ + $(".widget_save").click(function(ev){ console.log("in .widget_save") - event.preventDefault(); - event.stopPropagation(); + ev.preventDefault(); + ev.stopPropagation(); let pform = this.closest("form"); let data = new URLSearchParams(); for (const pair of new FormData(pform)) data.append(pair[0], pair[1]); diff --git a/query_gen/accumulator.go b/query_gen/accumulator.go index cbe9a976..46c6f30b 100644 --- a/query_gen/accumulator.go +++ b/query_gen/accumulator.go @@ -99,19 +99,19 @@ func (build *Accumulator) Tx(handler func(*TransactionBuilder) error) { build.RecordError(tx.Commit()) } -func (build *Accumulator) SimpleSelect(table string, columns string, where string, orderby string, limit string) *sql.Stmt { +func (build *Accumulator) SimpleSelect(table, columns, where, orderby, limit string) *sql.Stmt { return build.prepare(build.adapter.SimpleSelect("", table, columns, where, orderby, limit)) } -func (build *Accumulator) SimpleCount(table string, where string, limit string) *sql.Stmt { +func (build *Accumulator) SimpleCount(table, where, limit string) *sql.Stmt { return build.prepare(build.adapter.SimpleCount("", table, where, limit)) } -func (build *Accumulator) SimpleLeftJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) *sql.Stmt { +func (build *Accumulator) SimpleLeftJoin(table1, table2, columns, joiners, where, orderby, limit string) *sql.Stmt { return build.prepare(build.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit)) } -func (build *Accumulator) SimpleInnerJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) *sql.Stmt { +func (build *Accumulator) SimpleInnerJoin(table1, table2, columns, joiners, where, orderby, limit string) *sql.Stmt { return build.prepare(build.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit)) } @@ -119,7 +119,7 @@ func (build *Accumulator) CreateTable(table string, charset string, collation st return build.prepare(build.adapter.CreateTable("", table, charset, collation, columns, keys)) } -func (build *Accumulator) SimpleInsert(table string, columns string, fields string) *sql.Stmt { +func (build *Accumulator) SimpleInsert(table, columns, fields string) *sql.Stmt { return build.prepare(build.adapter.SimpleInsert("", table, columns, fields)) } @@ -135,15 +135,16 @@ func (build *Accumulator) SimpleInsertInnerJoin(ins DBInsert, sel DBJoin) *sql.S return build.prepare(build.adapter.SimpleInsertInnerJoin("", ins, sel)) } -func (build *Accumulator) SimpleUpdate(table string, set string, where string) *sql.Stmt { +func (build *Accumulator) SimpleUpdate(table, set, where string) *sql.Stmt { return build.prepare(build.adapter.SimpleUpdate(qUpdate(table, set, where))) } -func (build *Accumulator) SimpleUpdateSelect(table string, set string, where string) *sql.Stmt { - return build.prepare(build.adapter.SimpleUpdateSelect(qUpdate(table, set, where))) +func (build *Accumulator) SimpleUpdateSelect(table, set, table2, cols, where, orderby, limit string) *sql.Stmt { + pre := qUpdate(table, set, "").WhereQ(build.GetAdapter().Builder().Select().Table(table2).Columns(cols).Where(where).Orderby(orderby).Limit(limit)) + return build.prepare(build.adapter.SimpleUpdateSelect(pre)) } -func (build *Accumulator) SimpleDelete(table string, where string) *sql.Stmt { +func (build *Accumulator) SimpleDelete(table, where string) *sql.Stmt { return build.prepare(build.adapter.SimpleDelete("", table, where)) } @@ -256,7 +257,7 @@ type SimpleModel struct { func (build *Accumulator) SimpleModel(tbl, colstr, primary string) SimpleModel { var qlist, uplist string - for _, col := range strings.Split(colstr,",") { + for _, col := range strings.Split(colstr, ",") { qlist += "?," uplist += col + "=?," } @@ -267,9 +268,9 @@ func (build *Accumulator) SimpleModel(tbl, colstr, primary string) SimpleModel { where := primary + "=?" return SimpleModel{ - delete: build.Delete(tbl).Where(where).Prepare(), - create: build.Insert(tbl).Columns(colstr).Fields(qlist).Prepare(), - update: build.Update(tbl).Set(uplist).Where(where).Prepare(), + delete: build.Delete(tbl).Where(where).Prepare(), + create: build.Insert(tbl).Columns(colstr).Fields(qlist).Prepare(), + update: build.Update(tbl).Set(uplist).Where(where).Prepare(), } } @@ -298,11 +299,11 @@ func (m SimpleModel) CreateID(args ...interface{}) (int, error) { } func (build *Accumulator) Model(table string) *accModelBuilder { - return &accModelBuilder{table,"",build} + return &accModelBuilder{table, "", build} } type accModelBuilder struct { - table string + table string primary string build *Accumulator @@ -311,4 +312,4 @@ type accModelBuilder struct { func (b *accModelBuilder) Primary(col string) *accModelBuilder { b.primary = col return b -} \ No newline at end of file +} diff --git a/query_gen/mssql.go b/query_gen/mssql.go index 8818b1de..0b2ce577 100644 --- a/query_gen/mssql.go +++ b/query_gen/mssql.go @@ -44,7 +44,7 @@ func (a *MssqlAdapter) DbVersion() string { return "SELECT CONCAT(SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'))" } -func (a *MssqlAdapter) DropTable(name string, table string) (string, error) { +func (a *MssqlAdapter) DropTable(name, table string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -150,7 +150,7 @@ func (a *MssqlAdapter) AddColumn(name string, table string, column DBTableColumn // TODO: Implement this // TODO: Test to make sure everything works here -func (a *MssqlAdapter) AddIndex(name string, table string, iname string, colname string) (string, error) { +func (a *MssqlAdapter) AddIndex(name, table, iname, colname string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -194,27 +194,26 @@ func (a *MssqlAdapter) AddForeignKey(name string, table string, column string, f return "", errors.New("not implemented") } -func (a *MssqlAdapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) { +func (a *MssqlAdapter) SimpleInsert(name, table, cols, fields string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } q := "INSERT INTO [" + table + "] (" - if columns == "" { + if cols == "" { q += ") VALUES ()" a.pushStatement(name, "insert", q) return q, nil } // Escape the column names, just in case we've used a reserved keyword - for _, column := range processColumns(columns) { - if column.Type == "function" { - q += column.Left + "," + for _, col := range processColumns(cols) { + if col.Type == TokenFunc { + q += col.Left + "," } else { - q += "[" + column.Left + "]," + q += "[" + col.Left + "]," } } - // Remove the trailing comma q = q[0 : len(q)-1] q += ") VALUES (" @@ -314,17 +313,17 @@ func (a *MssqlAdapter) SimpleUpsert(name string, table string, columns string, f for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": + case TokenSub: q += " ?" - case "function", "operator", "number", "or": + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -337,14 +336,14 @@ func (a *MssqlAdapter) SimpleUpsert(name string, table string, columns string, f var fieldList string // Escape the column names, just in case we've used a reserved keyword - for columnID, column := range processColumns(columns) { + for columnID, col := range processColumns(columns) { fieldList += "f" + strconv.Itoa(columnID) + "," - if column.Type == "function" { - matched += column.Left + " = f" + strconv.Itoa(columnID) + "," - notMatched += column.Left + "," + if col.Type == TokenFunc { + matched += col.Left + " = f" + strconv.Itoa(columnID) + "," + notMatched += col.Left + "," } else { - matched += "[" + column.Left + "] = f" + strconv.Itoa(columnID) + "," - notMatched += "[" + column.Left + "]," + matched += "[" + col.Left + "] = f" + strconv.Itoa(columnID) + "," + notMatched += "[" + col.Left + "]," } } @@ -373,20 +372,20 @@ func (a *MssqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { q := "UPDATE [" + up.table + "] SET " for _, item := range processSet(up.set) { - q += "[" + item.Column + "] =" + q += "[" + item.Column + "]=" for _, token := range item.Expr { switch token.Type { - case "substitute": + case TokenSub: q += " ?" - case "function", "operator", "number", "or": + case TokenFunc, TokenOp, TokenNumber, TokenOr: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -394,7 +393,6 @@ func (a *MssqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { } q += "," } - // Remove the trailing comma q = q[0 : len(q)-1] // Add support for BETWEEN x.x @@ -403,15 +401,15 @@ func (a *MssqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { for _, loc := range processWhere(up.where) { for _, token := range loc.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -443,17 +441,17 @@ func (a *MssqlAdapter) SimpleDelete(name string, table string, where string) (st for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": + case TokenSub: q += " ?" - case "function", "operator", "number", "or": + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -492,7 +490,7 @@ func (a *MssqlAdapter) SimpleSelect(name string, table string, columns string, w if len(orderby) == 0 && limit != "" { return "", errors.New("Orderby needs to be set to use limit on Mssql") } - substituteCount := 0 + subCount := 0 q := "" // Escape the column names, just in case we've used a reserved keyword @@ -508,19 +506,19 @@ func (a *MssqlAdapter) SimpleSelect(name string, table string, columns string, w for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": - substituteCount++ - q += " ?" + strconv.Itoa(substituteCount) - case "function", "operator", "number", "or": + case TokenSub: + subCount++ + q += " ?" + strconv.Itoa(subCount) + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up // MSSQL seems to convert the formats? so we'll compare it with a regular date. Do this with the other methods too? if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -546,8 +544,8 @@ func (a *MssqlAdapter) SimpleSelect(name string, table string, columns string, w log.Printf("limiter: %+v\n", limiter) if limiter.Offset != "" { if limiter.Offset == "?" { - substituteCount++ - q += " OFFSET ?" + strconv.Itoa(substituteCount) + " ROWS" + subCount++ + q += " OFFSET ?" + strconv.Itoa(subCount) + " ROWS" } else { q += " OFFSET " + limiter.Offset + " ROWS" } @@ -556,8 +554,8 @@ func (a *MssqlAdapter) SimpleSelect(name string, table string, columns string, w // ! Does this work without an offset? if limiter.MaxCount != "" { if limiter.MaxCount == "?" { - substituteCount++ - limiter.MaxCount = "?" + strconv.Itoa(substituteCount) + subCount++ + limiter.MaxCount = "?" + strconv.Itoa(subCount) } q += " FETCH NEXT " + limiter.MaxCount + " ROWS ONLY " } @@ -594,22 +592,22 @@ func (a *MssqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, if len(orderby) == 0 && limit != "" { return "", errors.New("Orderby needs to be set to use limit on Mssql") } - substituteCount := 0 + subCount := 0 q := "" - for _, column := range processColumns(columns) { + for _, col := range processColumns(columns) { var source, alias string // Escape the column names, just in case we've used a reserved keyword - if column.Table != "" { - source = "[" + column.Table + "].[" + column.Left + "]" - } else if column.Type == "function" { - source = column.Left + if col.Table != "" { + source = "[" + col.Table + "].[" + col.Left + "]" + } else if col.Type == TokenFunc { + source = col.Left } else { - source = "[" + column.Left + "]" + source = "[" + col.Left + "]" } - if column.Alias != "" { - alias = " AS '" + column.Alias + "'" + if col.Alias != "" { + alias = " AS '" + col.Alias + "'" } q += source + alias + "," } @@ -629,23 +627,23 @@ func (a *MssqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": - substituteCount++ - q += " ?" + strconv.Itoa(substituteCount) - case "function", "operator", "number", "or": + case TokenSub: + subCount++ + q += " ?" + strconv.Itoa(subCount) + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: halves := strings.Split(token.Contents, ".") if len(halves) == 2 { q += " [" + halves[0] + "].[" + halves[1] + "]" } else { q += " [" + token.Contents + "]" } - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -676,8 +674,8 @@ func (a *MssqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, limiter := processLimit(limit) if limiter.Offset != "" { if limiter.Offset == "?" { - substituteCount++ - q += " OFFSET ?" + strconv.Itoa(substituteCount) + " ROWS" + subCount++ + q += " OFFSET ?" + strconv.Itoa(subCount) + " ROWS" } else { q += " OFFSET " + limiter.Offset + " ROWS" } @@ -686,8 +684,8 @@ func (a *MssqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, // ! Does this work without an offset? if limiter.MaxCount != "" { if limiter.MaxCount == "?" { - substituteCount++ - limiter.MaxCount = "?" + strconv.Itoa(substituteCount) + subCount++ + limiter.MaxCount = "?" + strconv.Itoa(subCount) } q += " FETCH NEXT " + limiter.MaxCount + " ROWS ONLY " } @@ -719,22 +717,22 @@ func (a *MssqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string if len(orderby) == 0 && limit != "" { return "", errors.New("Orderby needs to be set to use limit on Mssql") } - substituteCount := 0 + subCount := 0 q := "" - for _, column := range processColumns(columns) { + for _, col := range processColumns(columns) { var source, alias string // Escape the column names, just in case we've used a reserved keyword - if column.Table != "" { - source = "[" + column.Table + "].[" + column.Left + "]" - } else if column.Type == "function" { - source = column.Left + if col.Table != "" { + source = "[" + col.Table + "].[" + col.Left + "]" + } else if col.Type == TokenFunc { + source = col.Left } else { - source = "[" + column.Left + "]" + source = "[" + col.Left + "]" } - if column.Alias != "" { - alias = " AS '" + column.Alias + "'" + if col.Alias != "" { + alias = " AS '" + col.Alias + "'" } q += source + alias + "," } @@ -754,23 +752,23 @@ func (a *MssqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": - substituteCount++ - q += " ?" + strconv.Itoa(substituteCount) - case "function", "operator", "number", "or": + case TokenSub: + subCount++ + q += " ?" + strconv.Itoa(subCount) + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: halves := strings.Split(token.Contents, ".") if len(halves) == 2 { q += " [" + halves[0] + "].[" + halves[1] + "]" } else { q += " [" + token.Contents + "]" } - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -802,8 +800,8 @@ func (a *MssqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string limiter := processLimit(limit) if limiter.Offset != "" { if limiter.Offset == "?" { - substituteCount++ - q += " OFFSET ?" + strconv.Itoa(substituteCount) + " ROWS" + subCount++ + q += " OFFSET ?" + strconv.Itoa(subCount) + " ROWS" } else { q += " OFFSET " + limiter.Offset + " ROWS" } @@ -812,8 +810,8 @@ func (a *MssqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string // ! Does this work without an offset? if limiter.MaxCount != "" { if limiter.MaxCount == "?" { - substituteCount++ - limiter.MaxCount = "?" + strconv.Itoa(substituteCount) + subCount++ + limiter.MaxCount = "?" + strconv.Itoa(subCount) } q += " FETCH NEXT " + limiter.MaxCount + " ROWS ONLY " } @@ -839,28 +837,28 @@ func (a *MssqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel DBSelec q := "INSERT INTO [" + ins.Table + "] (" // Escape the column names, just in case we've used a reserved keyword - for _, column := range processColumns(ins.Columns) { - if column.Type == "function" { - q += column.Left + "," + for _, col := range processColumns(ins.Columns) { + if col.Type == TokenFunc { + q += col.Left + "," } else { - q += "[" + column.Left + "]," + q += "[" + col.Left + "]," } } q = q[0:len(q)-1] + ") SELECT " /* Select */ - substituteCount := 0 + subCount := 0 - for _, column := range processColumns(sel.Columns) { + for _, col := range processColumns(sel.Columns) { var source, alias string // Escape the column names, just in case we've used a reserved keyword - if column.Type == "function" || column.Type == "substitute" { - source = column.Left + if col.Type == TokenFunc || col.Type == TokenSub { + source = col.Left } else { - source = "[" + column.Left + "]" + source = "[" + col.Left + "]" } - if column.Alias != "" { - alias = " AS [" + column.Alias + "]" + if col.Alias != "" { + alias = " AS [" + col.Alias + "]" } q += " " + source + alias + "," } @@ -872,18 +870,18 @@ func (a *MssqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel DBSelec for _, loc := range processWhere(sel.Where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": - substituteCount++ - q += " ?" + strconv.Itoa(substituteCount) - case "function", "operator", "number", "or": + case TokenSub: + subCount++ + q += " ?" + strconv.Itoa(subCount) + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -913,8 +911,8 @@ func (a *MssqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel DBSelec limiter := processLimit(sel.Limit) if limiter.Offset != "" { if limiter.Offset == "?" { - substituteCount++ - q += " OFFSET ?" + strconv.Itoa(substituteCount) + " ROWS" + subCount++ + q += " OFFSET ?" + strconv.Itoa(subCount) + " ROWS" } else { q += " OFFSET " + limiter.Offset + " ROWS" } @@ -923,8 +921,8 @@ func (a *MssqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel DBSelec // ! Does this work without an offset? if limiter.MaxCount != "" { if limiter.MaxCount == "?" { - substituteCount++ - limiter.MaxCount = "?" + strconv.Itoa(substituteCount) + subCount++ + limiter.MaxCount = "?" + strconv.Itoa(subCount) } q += " FETCH NEXT " + limiter.MaxCount + " ROWS ONLY " } @@ -950,30 +948,30 @@ func (a *MssqlAdapter) simpleJoin(name string, ins DBInsert, sel DBJoin, joinTyp q := "INSERT INTO [" + ins.Table + "] (" // Escape the column names, just in case we've used a reserved keyword - for _, column := range processColumns(ins.Columns) { - if column.Type == "function" { - q += column.Left + "," + for _, col := range processColumns(ins.Columns) { + if col.Type == TokenFunc { + q += col.Left + "," } else { - q += "[" + column.Left + "]," + q += "[" + col.Left + "]," } } q = q[0:len(q)-1] + ") SELECT " /* Select */ - substituteCount := 0 + subCount := 0 - for _, column := range processColumns(sel.Columns) { + for _, col := range processColumns(sel.Columns) { var source, alias string // Escape the column names, just in case we've used a reserved keyword - if column.Table != "" { - source = "[" + column.Table + "].[" + column.Left + "]" - } else if column.Type == "function" { - source = column.Left + if col.Table != "" { + source = "[" + col.Table + "].[" + col.Left + "]" + } else if col.Type == TokenFunc { + source = col.Left } else { - source = "[" + column.Left + "]" + source = "[" + col.Left + "]" } - if column.Alias != "" { - alias = " AS '" + column.Alias + "'" + if col.Alias != "" { + alias = " AS '" + col.Alias + "'" } q += source + alias + "," } @@ -993,23 +991,23 @@ func (a *MssqlAdapter) simpleJoin(name string, ins DBInsert, sel DBJoin, joinTyp for _, loc := range processWhere(sel.Where) { for _, token := range loc.Expr { switch token.Type { - case "substitute": - substituteCount++ - q += " ?" + strconv.Itoa(substituteCount) - case "function", "operator", "number", "or": + case TokenSub: + subCount++ + q += " ?" + strconv.Itoa(subCount) + case TokenFunc, TokenOp, TokenNumber, TokenOr, TokenNot, TokenLike: // TODO: Split the function case off to speed things up if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: halves := strings.Split(token.Contents, ".") if len(halves) == 2 { q += " [" + halves[0] + "].[" + halves[1] + "]" } else { q += " [" + token.Contents + "]" } - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -1040,8 +1038,8 @@ func (a *MssqlAdapter) simpleJoin(name string, ins DBInsert, sel DBJoin, joinTyp limiter := processLimit(sel.Limit) if limiter.Offset != "" { if limiter.Offset == "?" { - substituteCount++ - q += " OFFSET ?" + strconv.Itoa(substituteCount) + " ROWS" + subCount++ + q += " OFFSET ?" + strconv.Itoa(subCount) + " ROWS" } else { q += " OFFSET " + limiter.Offset + " ROWS" } @@ -1050,8 +1048,8 @@ func (a *MssqlAdapter) simpleJoin(name string, ins DBInsert, sel DBJoin, joinTyp // ! Does this work without an offset? if limiter.MaxCount != "" { if limiter.MaxCount == "?" { - substituteCount++ - limiter.MaxCount = "?" + strconv.Itoa(substituteCount) + subCount++ + limiter.MaxCount = "?" + strconv.Itoa(subCount) } q += " FETCH NEXT " + limiter.MaxCount + " ROWS ONLY " } @@ -1074,7 +1072,7 @@ func (a *MssqlAdapter) SimpleInsertInnerJoin(name string, ins DBInsert, sel DBJo return a.simpleJoin(name, ins, sel, "INNER") } -func (a *MssqlAdapter) SimpleCount(name string, table string, where string, limit string) (string, error) { +func (a *MssqlAdapter) SimpleCount(name, table, where, limit string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -1086,14 +1084,14 @@ func (a *MssqlAdapter) SimpleCount(name string, table string, where string, limi for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot, TokenLike: if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "GETUTCDATE()" } q += " " + token.Contents - case "column": + case TokenColumn: q += " [" + token.Contents + "]" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") diff --git a/query_gen/mysql.go b/query_gen/mysql.go index 8d99e7cc..9d2ca9f7 100644 --- a/query_gen/mysql.go +++ b/query_gen/mysql.go @@ -27,20 +27,20 @@ type MysqlAdapter struct { } // GetName gives you the name of the database adapter. In this case, it's mysql -func (adapter *MysqlAdapter) GetName() string { - return adapter.Name +func (a *MysqlAdapter) GetName() string { + return a.Name } -func (adapter *MysqlAdapter) GetStmt(name string) DBStmt { - return adapter.Buffer[name] +func (a *MysqlAdapter) GetStmt(name string) DBStmt { + return a.Buffer[name] } -func (adapter *MysqlAdapter) GetStmts() map[string]DBStmt { - return adapter.Buffer +func (a *MysqlAdapter) GetStmts() map[string]DBStmt { + return a.Buffer } // TODO: Add an option to disable unix pipes -func (adapter *MysqlAdapter) BuildConn(config map[string]string) (*sql.DB, error) { +func (a *MysqlAdapter) BuildConn(config map[string]string) (*sql.DB, error) { dbCollation, ok := config["collation"] if !ok { return nil, ErrNoCollation @@ -78,21 +78,21 @@ func (adapter *MysqlAdapter) BuildConn(config map[string]string) (*sql.DB, error return db, db.Ping() } -func (adapter *MysqlAdapter) DbVersion() string { +func (a *MysqlAdapter) DbVersion() string { return "SELECT VERSION()" } -func (adapter *MysqlAdapter) DropTable(name string, table string) (string, error) { +func (a *MysqlAdapter) DropTable(name string, table string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } - querystr := "DROP TABLE IF EXISTS `" + table + "`;" + q := "DROP TABLE IF EXISTS `" + table + "`;" // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - adapter.pushStatement(name, "drop-table", querystr) - return querystr, nil + a.pushStatement(name, "drop-table", q) + return q, nil } -func (adapter *MysqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { +func (a *MysqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -100,49 +100,50 @@ func (adapter *MysqlAdapter) CreateTable(name string, table string, charset stri return "", errors.New("You can't have a table with no columns") } - var querystr = "CREATE TABLE `" + table + "` (" + q := "CREATE TABLE `" + table + "` (" for _, column := range columns { - column, size, end := adapter.parseColumn(column) - querystr += "\n\t`" + column.Name + "` " + column.Type + size + end + "," + column, size, end := a.parseColumn(column) + q += "\n\t`" + column.Name + "` " + column.Type + size + end + "," } if len(keys) > 0 { for _, key := range keys { - querystr += "\n\t" + key.Type + q += "\n\t" + key.Type if key.Type != "unique" { - querystr += " key" + q += " key" } if key.Type == "foreign" { cols := strings.Split(key.Columns, ",") - querystr += "(`" + cols[0] + "`) REFERENCES `" + key.FTable + "`(`" + cols[1] + "`)" + q += "(`" + cols[0] + "`) REFERENCES `" + key.FTable + "`(`" + cols[1] + "`)" if key.Cascade { - querystr += " ON DELETE CASCADE" + q += " ON DELETE CASCADE" } - querystr += "," + q += "," } else { - querystr += "(" + q += "(" for _, column := range strings.Split(key.Columns, ",") { - querystr += "`" + column + "`," + q += "`" + column + "`," } - querystr = querystr[0:len(querystr)-1] + ")," + q = q[0:len(q)-1] + ")," } } } - querystr = querystr[0:len(querystr)-1] + "\n)" + q = q[0:len(q)-1] + "\n)" if charset != "" { - querystr += " CHARSET=" + charset + q += " CHARSET=" + charset } if collation != "" { - querystr += " COLLATE " + collation + q += " COLLATE " + collation } // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - adapter.pushStatement(name, "create-table", querystr+";") - return querystr + ";", nil + q += ";" + a.pushStatement(name, "create-table", q) + return q, nil } -func (adapter *MysqlAdapter) parseColumn(column DBTableColumn) (col DBTableColumn, size string, end string) { +func (a *MysqlAdapter) parseColumn(column DBTableColumn) (col DBTableColumn, size string, end string) { // Make it easier to support Cassandra in the future if column.Type == "createdAt" { column.Type = "datetime" @@ -162,7 +163,7 @@ func (adapter *MysqlAdapter) parseColumn(column DBTableColumn) (col DBTableColum end = " DEFAULT " /*if column.Type == "datetime" && column.Default[len(column.Default)-1] == ')' { end += column.Default - } else */if adapter.stringyType(column.Type) && column.Default != "''" { + } else */if a.stringyType(column.Type) && column.Default != "''" { end += "'" + column.Default + "'" } else { end += column.Default @@ -188,20 +189,20 @@ func (a *MysqlAdapter) AddColumn(name string, table string, column DBTableColumn } column, size, end := a.parseColumn(column) - querystr := "ALTER TABLE `" + table + "` ADD COLUMN " + "`" + column.Name + "` " + column.Type + size + end + q := "ALTER TABLE `" + table + "` ADD COLUMN " + "`" + column.Name + "` " + column.Type + size + end if key != nil { - querystr += " " + key.Type + q += " " + key.Type if key.Type != "unique" { - querystr += " key" + q += " key" } else if key.Type == "primary" { - querystr += " first" + q += " first" } } // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - a.pushStatement(name, "add-column", querystr) - return querystr, nil + a.pushStatement(name, "add-column", q) + return q, nil } // TODO: Test to make sure everything works here @@ -216,10 +217,10 @@ func (a *MysqlAdapter) AddIndex(name string, table string, iname string, colname return "", errors.New("You need a name for the column") } - querystr := "ALTER TABLE `" + table + "` ADD INDEX " + "`i_" + iname + "` (`" + colname + "`);" + q := "ALTER TABLE `" + table + "` ADD INDEX " + "`i_" + iname + "` (`" + colname + "`);" // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - a.pushStatement(name, "add-index", querystr) - return querystr, nil + a.pushStatement(name, "add-index", q) + return q, nil } // TODO: Test to make sure everything works here @@ -228,45 +229,46 @@ func (a *MysqlAdapter) AddKey(name string, table string, column string, key DBTa if table == "" { return "", errors.New("You need a name for this table") } - var querystr string + var q string if key.Type == "fulltext" { - querystr = "ALTER TABLE `" + table + "` ADD FULLTEXT(`" + column + "`)" + q = "ALTER TABLE `" + table + "` ADD FULLTEXT(`" + column + "`)" } else { return "", errors.New("Only fulltext is supported by AddKey right now") } // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - a.pushStatement(name, "add-key", querystr) - return querystr, nil + a.pushStatement(name, "add-key", q) + return q, nil } func (a *MysqlAdapter) AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error) { - var c = func(str string, val bool) { + c := func(str string, val bool) { if e != nil || !val { return } - e = errors.New("You need a "+str+" for this table") + e = errors.New("You need a " + str + " for this table") } - c("name",table=="") - c("column",column=="") - c("ftable",ftable=="") - c("fcolumn",fcolumn=="") + c("name", table == "") + c("column", column == "") + c("ftable", ftable == "") + c("fcolumn", fcolumn == "") if e != nil { return "", e } - querystr := "ALTER TABLE `"+table+"` ADD CONSTRAINT `fk_"+column+"` FOREIGN KEY(`"+column+"`) REFERENCES `"+ftable+"`(`"+fcolumn+"`)" + q := "ALTER TABLE `" + table + "` ADD CONSTRAINT `fk_" + column + "` FOREIGN KEY(`" + column + "`) REFERENCES `" + ftable + "`(`" + fcolumn + "`)" if cascade { - querystr += " ON DELETE CASCADE" + q += " ON DELETE CASCADE" } // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - a.pushStatement(name, "add-foreign-key", querystr) - return querystr, nil + a.pushStatement(name, "add-foreign-key", q) + return q, nil } var silen1 = len("INSERT INTO ``() VALUES () ") -func (adapter *MysqlAdapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) { + +func (a *MysqlAdapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -277,7 +279,7 @@ func (adapter *MysqlAdapter) SimpleInsert(name string, table string, columns str sb.WriteString(table) sb.WriteString("`(") if columns != "" { - sb.WriteString(adapter.buildColumns(columns)) + sb.WriteString(a.buildColumns(columns)) sb.WriteString(") VALUES (") fs := processFields(fields) sb.Grow(len(fs) * 3) @@ -301,27 +303,27 @@ func (adapter *MysqlAdapter) SimpleInsert(name string, table string, columns str // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator q := sb.String() - adapter.pushStatement(name, "insert", q) + a.pushStatement(name, "insert", q) return q, nil } -func (adapter *MysqlAdapter) buildColumns(columns string) (querystr string) { +func (a *MysqlAdapter) buildColumns(columns string) (q string) { if columns == "" { return "" } // Escape the column names, just in case we've used a reserved keyword - for _, column := range processColumns(columns) { - if column.Type == "function" { - querystr += column.Left + "," + for _, col := range processColumns(columns) { + if col.Type == TokenFunc { + q += col.Left + "," } else { - querystr += "`" + column.Left + "`," + q += "`" + col.Left + "`," } } - return querystr[0 : len(querystr)-1] + return q[0 : len(q)-1] } // ! DEPRECATED -func (adapter *MysqlAdapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) { +func (a *MysqlAdapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -332,18 +334,18 @@ func (adapter *MysqlAdapter) SimpleReplace(name string, table string, columns st return "", errors.New("No input data found for SimpleInsert") } - var querystr = "REPLACE INTO `" + table + "`(" + adapter.buildColumns(columns) + ") VALUES (" + q := "REPLACE INTO `" + table + "`(" + a.buildColumns(columns) + ") VALUES (" for _, field := range processFields(fields) { - querystr += field.Name + "," + q += field.Name + "," } - querystr = querystr[0 : len(querystr)-1] + q = q[0 : len(q)-1] // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - adapter.pushStatement(name, "replace", querystr+")") - return querystr + ")", nil + a.pushStatement(name, "replace", q+")") + return q + ")", nil } -func (adapter *MysqlAdapter) SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error) { +func (a *MysqlAdapter) SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -356,24 +358,24 @@ func (adapter *MysqlAdapter) SimpleUpsert(name string, table string, columns str if where == "" { return "", errors.New("You need a where for this upsert") } - - var querystr = "INSERT INTO `" + table + "`(" - var parsedFields = processFields(fields) + + q := "INSERT INTO `" + table + "`(" + parsedFields := processFields(fields) var insertColumns string var insertValues string - var setBit = ") ON DUPLICATE KEY UPDATE " + setBit := ") ON DUPLICATE KEY UPDATE " - for columnID, column := range processColumns(columns) { + for columnID, col := range processColumns(columns) { field := parsedFields[columnID] - if column.Type == "function" { - insertColumns += column.Left + "," + if col.Type == TokenFunc { + insertColumns += col.Left + "," insertValues += field.Name + "," - setBit += column.Left + " = " + field.Name + " AND " + setBit += col.Left + " = " + field.Name + " AND " } else { - insertColumns += "`" + column.Left + "`," + insertColumns += "`" + col.Left + "`," insertValues += field.Name + "," - setBit += "`" + column.Left + "` = " + field.Name + " AND " + setBit += "`" + col.Left + "` = " + field.Name + " AND " } } insertColumns = insertColumns[0 : len(insertColumns)-1] @@ -381,15 +383,16 @@ func (adapter *MysqlAdapter) SimpleUpsert(name string, table string, columns str insertColumns += ") VALUES (" + insertValues setBit = setBit[0 : len(setBit)-5] - querystr += insertColumns + setBit + q += insertColumns + setBit // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - adapter.pushStatement(name, "upsert", querystr) - return querystr, nil + a.pushStatement(name, "upsert", q) + return q, nil } var sulen1 = len("UPDATE `` SET ") -func (adapter *MysqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { + +func (a *MysqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { if up.table == "" { return "", errors.New("You need a name for this table") } @@ -411,17 +414,17 @@ func (adapter *MysqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) sb.WriteString("`") } sb.WriteString(item.Column) - sb.WriteString("` =") + sb.WriteString("`=") for _, token := range item.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr: sb.WriteString(" ") sb.WriteString(token.Contents) - case "column": + case TokenColumn: sb.WriteString(" `") sb.WriteString(token.Contents) sb.WriteString("`") - case "string": + case TokenString: sb.WriteString(" '") sb.WriteString(token.Contents) sb.WriteString("'") @@ -429,7 +432,7 @@ func (adapter *MysqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) } } - whereStr, err := adapter.buildFlexiWhere(up.where,up.dateCutoff) + whereStr, err := a.buildFlexiWhere(up.where, up.dateCutoff) sb.WriteString(whereStr) if err != nil { return sb.String(), err @@ -437,29 +440,28 @@ func (adapter *MysqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator q := sb.String() - adapter.pushStatement(up.name, "update", q) + a.pushStatement(up.name, "update", q) return q, nil } -func (adapter *MysqlAdapter) SimpleDelete(name string, table string, where string) (string, error) { +func (a *MysqlAdapter) SimpleDelete(name string, table string, where string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } if where == "" { return "", errors.New("You need to specify what data you want to delete") } - - var q = "DELETE FROM `" + table + "` WHERE" + q := "DELETE FROM `" + table + "` WHERE" // Add support for BETWEEN x.x for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot: q += " " + token.Contents - case "column": + case TokenColumn: q += " `" + token.Contents + "`" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") @@ -470,41 +472,41 @@ func (adapter *MysqlAdapter) SimpleDelete(name string, table string, where strin q = strings.TrimSpace(q[0 : len(q)-4]) // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - adapter.pushStatement(name, "delete", q) + a.pushStatement(name, "delete", q) return q, nil } -func (adapter *MysqlAdapter) ComplexDelete(b *deletePrebuilder) (string, error) { +func (a *MysqlAdapter) ComplexDelete(b *deletePrebuilder) (string, error) { if b.table == "" { return "", errors.New("You need a name for this table") } if b.where == "" && b.dateCutoff == nil { return "", errors.New("You need to specify what data you want to delete") } - var q = "DELETE FROM `" + b.table + "`" + q := "DELETE FROM `" + b.table + "`" - whereStr, err := adapter.buildFlexiWhere(b.where, b.dateCutoff) + whereStr, err := a.buildFlexiWhere(b.where, b.dateCutoff) if err != nil { return q, err } q += whereStr // TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator - adapter.pushStatement(b.name, "delete", q) + a.pushStatement(b.name, "delete", q) return q, nil } // We don't want to accidentally wipe tables, so we'll have a separate method for purging tables instead -func (adapter *MysqlAdapter) Purge(name string, table string) (string, error) { +func (a *MysqlAdapter) Purge(name string, table string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } - q := "DELETE FROM `"+table+"`" - adapter.pushStatement(name, "purge", q) + q := "DELETE FROM `" + table + "`" + a.pushStatement(name, "purge", q) return q, nil } -func (adapter *MysqlAdapter) buildWhere(where string) (q string, err error) { +func (a *MysqlAdapter) buildWhere(where string) (q string, err error) { if len(where) == 0 { return "", nil } @@ -512,11 +514,11 @@ func (adapter *MysqlAdapter) buildWhere(where string) (q string, err error) { for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot, TokenLike: q += " " + token.Contents - case "column": + case TokenColumn: q += " `" + token.Contents + "`" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: return q, errors.New("This token doesn't exist o_o") @@ -528,11 +530,10 @@ func (adapter *MysqlAdapter) buildWhere(where string) (q string, err error) { } // The new version of buildWhere() currently only used in ComplexSelect for complex OO builder queries -func (adapter *MysqlAdapter) buildFlexiWhere(where string, dateCutoff *dateCutoff) (q string, err error) { +func (a *MysqlAdapter) buildFlexiWhere(where string, dateCutoff *dateCutoff) (q string, err error) { if len(where) == 0 && dateCutoff == nil { return "", nil } - q = " WHERE" if dateCutoff != nil { if dateCutoff.Type == 0 { @@ -546,11 +547,11 @@ func (adapter *MysqlAdapter) buildFlexiWhere(where string, dateCutoff *dateCutof for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot, TokenLike: q += " " + token.Contents - case "column": + case TokenColumn: q += " `" + token.Contents + "`" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: return q, errors.New("This token doesn't exist o_o") @@ -563,7 +564,7 @@ func (adapter *MysqlAdapter) buildFlexiWhere(where string, dateCutoff *dateCutof return q[0 : len(q)-4], nil } -func (adapter *MysqlAdapter) buildOrderby(orderby string) (q string) { +func (a *MysqlAdapter) buildOrderby(orderby string) (q string) { if len(orderby) != 0 { q = " ORDER BY " for _, column := range processOrderby(orderby) { @@ -575,14 +576,14 @@ func (adapter *MysqlAdapter) buildOrderby(orderby string) (q string) { return q } -func (adapter *MysqlAdapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) { +func (a *MysqlAdapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } if len(columns) == 0 { return "", errors.New("No columns found for SimpleSelect") } - var q = "SELECT " + q := "SELECT " // Slice up the user friendly strings into something easier to process for _, column := range strings.Split(strings.TrimSpace(columns), ",") { @@ -590,20 +591,20 @@ func (adapter *MysqlAdapter) SimpleSelect(name string, table string, columns str } q = q[0 : len(q)-1] - whereStr, err := adapter.buildWhere(where) + whereStr, err := a.buildWhere(where) if err != nil { return q, err } - q += " FROM `" + table + "`" + whereStr + adapter.buildOrderby(orderby) + adapter.buildLimit(limit) + q += " FROM `" + table + "`" + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) q = strings.TrimSpace(q) - adapter.pushStatement(name, "select", q) + a.pushStatement(name, "select", q) return q, nil } func (a *MysqlAdapter) ComplexSelect(preBuilder *selectPrebuilder) (out string, err error) { sb := &strings.Builder{} - err = a.complexSelect(preBuilder,sb) + err = a.complexSelect(preBuilder, sb) out = sb.String() a.pushStatement(preBuilder.name, "select", out) return out, err @@ -611,6 +612,7 @@ func (a *MysqlAdapter) ComplexSelect(preBuilder *selectPrebuilder) (out string, var cslen1 = len("SELECT FROM ``") var cslen2 = len(" WHERE `` IN(") + func (a *MysqlAdapter) complexSelect(preBuilder *selectPrebuilder, sb *strings.Builder) error { if preBuilder.table == "" { return errors.New("You need a name for this table") @@ -618,7 +620,7 @@ func (a *MysqlAdapter) complexSelect(preBuilder *selectPrebuilder, sb *strings.B if len(preBuilder.columns) == 0 { return errors.New("No columns found for ComplexSelect") } - + cols := a.buildJoinColumns(preBuilder.columns) sb.Grow(cslen1 + len(cols) + len(preBuilder.table)) sb.WriteString("SELECT ") @@ -633,7 +635,7 @@ func (a *MysqlAdapter) complexSelect(preBuilder *selectPrebuilder, sb *strings.B sb.WriteString(" WHERE `") sb.WriteString(preBuilder.inColumn) sb.WriteString("` IN(") - err := a.complexSelect(preBuilder.inChain,sb) + err := a.complexSelect(preBuilder.inChain, sb) if err != nil { return err } @@ -673,18 +675,18 @@ func (a *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, return "", err } - thalf1 := strings.Split(strings.Replace(table1," as ", " AS ",-1)," AS ") + thalf1 := strings.Split(strings.Replace(table1, " as ", " AS ", -1), " AS ") var as1 string if len(thalf1) == 2 { - as1 = " AS `"+ thalf1[1]+"`" + as1 = " AS `" + thalf1[1] + "`" } - thalf2 := strings.Split(strings.Replace(table2," as ", " AS ",-1)," AS ") + thalf2 := strings.Split(strings.Replace(table2, " as ", " AS ", -1), " AS ") var as2 string if len(thalf2) == 2 { - as2 = " AS `"+ thalf2[1]+"`" + as2 = " AS `" + thalf2[1] + "`" } - q := "SELECT" + a.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`"+as1+" LEFT JOIN `" + thalf2[0] + "`"+as2+" ON " + a.buildJoiners(joiners) + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) + q := "SELECT" + a.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`" + as1 + " LEFT JOIN `" + thalf2[0] + "`" + as2 + " ON " + a.buildJoiners(joiners) + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) q = strings.TrimSpace(q) a.pushStatement(name, "select", q) @@ -710,107 +712,104 @@ func (a *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string return "", err } - thalf1 := strings.Split(strings.Replace(table1," as ", " AS ",-1)," AS ") + thalf1 := strings.Split(strings.Replace(table1, " as ", " AS ", -1), " AS ") var as1 string if len(thalf1) == 2 { - as1 = " AS `"+ thalf1[1]+"`" + as1 = " AS `" + thalf1[1] + "`" } - thalf2 := strings.Split(strings.Replace(table2," as ", " AS ",-1)," AS ") + thalf2 := strings.Split(strings.Replace(table2, " as ", " AS ", -1), " AS ") var as2 string if len(thalf2) == 2 { - as2 = " AS `"+ thalf2[1]+"`" + as2 = " AS `" + thalf2[1] + "`" } - q := "SELECT " + a.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`"+as1+" INNER JOIN `" + thalf2[0] + "`"+as2+" ON " + a.buildJoiners(joiners) + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) + q := "SELECT " + a.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`" + as1 + " INNER JOIN `" + thalf2[0] + "`" + as2 + " ON " + a.buildJoiners(joiners) + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) q = strings.TrimSpace(q) a.pushStatement(name, "select", q) return q, nil } -func (adapter *MysqlAdapter) SimpleUpdateSelect(up *updatePrebuilder) (string, error) { +func (a *MysqlAdapter) SimpleUpdateSelect(up *updatePrebuilder) (string, error) { sel := up.whereSubQuery - whereStr, err := adapter.buildWhere(sel.where) + whereStr, err := a.buildWhere(sel.where) if err != nil { return "", err } var setter string for _, item := range processSet(up.set) { - setter += "`" + item.Column + "` =" + setter += "`" + item.Column + "`=" for _, token := range item.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": - setter += " " + token.Contents - case "column": - setter += " `" + token.Contents + "`" - case "string": - setter += " '" + token.Contents + "'" + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr: + setter += token.Contents + case TokenColumn: + setter += "`" + token.Contents + "`" + case TokenString: + setter += "'" + token.Contents + "'" } } setter += "," } setter = setter[0 : len(setter)-1] - var querystr = "UPDATE `" + up.table + "` SET " + setter + " WHERE (SELECT" + adapter.buildJoinColumns(sel.columns) + " FROM `" + sel.table + "`" + whereStr + adapter.buildOrderby(sel.orderby) + adapter.buildLimit(sel.limit) + ")" - - querystr = strings.TrimSpace(querystr) - adapter.pushStatement(up.name, "update", querystr) - return querystr, nil -} - -func (adapter *MysqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel DBSelect) (string, error) { - whereStr, err := adapter.buildWhere(sel.Where) - if err != nil { - return "", err - } - - var q = "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table + "`" + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) - + q := "UPDATE `" + up.table + "` SET " + setter + " WHERE (SELECT" + a.buildJoinColumns(sel.columns) + " FROM `" + sel.table + "`" + whereStr + a.buildOrderby(sel.orderby) + a.buildLimit(sel.limit) + ")" q = strings.TrimSpace(q) - adapter.pushStatement(name, "insert", q) + a.pushStatement(up.name, "update", q) return q, nil } -func (adapter *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DBInsert, sel DBJoin) (string, error) { - whereStr, err := adapter.buildJoinWhere(sel.Where) +func (a *MysqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel DBSelect) (string, error) { + whereStr, err := a.buildWhere(sel.Where) if err != nil { return "", err } - q := "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) - + q := "INSERT INTO `" + ins.Table + "`(" + a.buildColumns(ins.Columns) + ") SELECT" + a.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table + "`" + whereStr + a.buildOrderby(sel.Orderby) + a.buildLimit(sel.Limit) q = strings.TrimSpace(q) - adapter.pushStatement(name, "insert", q) + a.pushStatement(name, "insert", q) + return q, nil +} + +func (a *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DBInsert, sel DBJoin) (string, error) { + whereStr, err := a.buildJoinWhere(sel.Where) + if err != nil { + return "", err + } + + q := "INSERT INTO `" + ins.Table + "`(" + a.buildColumns(ins.Columns) + ") SELECT" + a.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " + a.buildJoiners(sel.Joiners) + whereStr + a.buildOrderby(sel.Orderby) + a.buildLimit(sel.Limit) + q = strings.TrimSpace(q) + a.pushStatement(name, "insert", q) return q, nil } // TODO: Make this more consistent with the other build* methods? -func (adapter *MysqlAdapter) buildJoiners(joiners string) (q string) { - for _, joiner := range processJoiner(joiners) { - q += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " +func (a *MysqlAdapter) buildJoiners(joiners string) (q string) { + for _, j := range processJoiner(joiners) { + q += "`" + j.LeftTable + "`.`" + j.LeftColumn + "` " + j.Operator + " `" + j.RightTable + "`.`" + j.RightColumn + "` AND " } // Remove the trailing AND return q[0 : len(q)-4] } // Add support for BETWEEN x.x -func (adapter *MysqlAdapter) buildJoinWhere(where string) (q string, err error) { +func (a *MysqlAdapter) buildJoinWhere(where string) (q string, err error) { if len(where) != 0 { q = " WHERE" for _, loc := range processWhere(where) { for _, token := range loc.Expr { switch token.Type { - case "function", "operator", "number", "substitute", "or": + case TokenFunc, TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot, TokenLike: q += " " + token.Contents - case "column": + case TokenColumn: halves := strings.Split(token.Contents, ".") if len(halves) == 2 { q += " `" + halves[0] + "`.`" + halves[1] + "`" } else { q += " `" + token.Contents + "`" } - case "string": + case TokenString: q += " '" + token.Contents + "'" default: return q, errors.New("This token doesn't exist o_o") @@ -823,74 +822,73 @@ func (adapter *MysqlAdapter) buildJoinWhere(where string) (q string, err error) return q, nil } -func (adapter *MysqlAdapter) buildLimit(limit string) (q string) { +func (a *MysqlAdapter) buildLimit(limit string) (q string) { if limit != "" { q = " LIMIT " + limit } return q } -func (a *MysqlAdapter) buildJoinColumns(columns string) (q string) { - for _, column := range processColumns(columns) { +func (a *MysqlAdapter) buildJoinColumns(cols string) (q string) { + for _, col := range processColumns(cols) { // TODO: Move the stirng and number logic to processColumns? // TODO: Error if [0] doesn't exist - firstChar := column.Left[0] + firstChar := col.Left[0] if firstChar == '\'' { - column.Type = "string" + col.Type = TokenString } else { _, err := strconv.Atoi(string(firstChar)) if err == nil { - column.Type = "number" + col.Type = TokenNumber } } // Escape the column names, just in case we've used a reserved keyword - var source = column.Left - if column.Table != "" { - source = "`" + column.Table + "`.`" + source + "`" - } else if column.Type != "function" && column.Type != "number" && column.Type != "substitute" && column.Type != "string" { + source := col.Left + if col.Table != "" { + source = "`" + col.Table + "`.`" + source + "`" + } else if col.Type != TokenFunc && col.Type != TokenNumber && col.Type != TokenSub && col.Type != TokenString { source = "`" + source + "`" } var alias string - if column.Alias != "" { - alias = " AS `" + column.Alias + "`" + if col.Alias != "" { + alias = " AS `" + col.Alias + "`" } q += " " + source + alias + "," } return q[0 : len(q)-1] } -func (adapter *MysqlAdapter) SimpleInsertInnerJoin(name string, ins DBInsert, sel DBJoin) (string, error) { - whereStr, err := adapter.buildJoinWhere(sel.Where) +func (a *MysqlAdapter) SimpleInsertInnerJoin(name string, ins DBInsert, sel DBJoin) (string, error) { + whereStr, err := a.buildJoinWhere(sel.Where) if err != nil { return "", err } - q := "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) - + q := "INSERT INTO `" + ins.Table + "`(" + a.buildColumns(ins.Columns) + ") SELECT" + a.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " + a.buildJoiners(sel.Joiners) + whereStr + a.buildOrderby(sel.Orderby) + a.buildLimit(sel.Limit) q = strings.TrimSpace(q) - adapter.pushStatement(name, "insert", q) + a.pushStatement(name, "insert", q) return q, nil } -func (adapter *MysqlAdapter) SimpleCount(name string, table string, where string, limit string) (q string, err error) { +func (a *MysqlAdapter) SimpleCount(name string, table string, where string, limit string) (q string, err error) { if table == "" { return "", errors.New("You need a name for this table") } - whereStr, err := adapter.buildWhere(where) + whereStr, err := a.buildWhere(where) if err != nil { return "", err } - q = "SELECT COUNT(*) FROM `" + table + "`" + whereStr + adapter.buildLimit(limit) + q = "SELECT COUNT(*) FROM `" + table + "`" + whereStr + a.buildLimit(limit) q = strings.TrimSpace(q) - adapter.pushStatement(name, "select", q) + a.pushStatement(name, "select", q) return q, nil } -func (adapter *MysqlAdapter) Builder() *prebuilder { - return &prebuilder{adapter} +func (a *MysqlAdapter) Builder() *prebuilder { + return &prebuilder{a} } func (a *MysqlAdapter) Write() error { @@ -964,7 +962,7 @@ func (a *MysqlAdapter) pushStatement(name string, stype string, querystr string) a.BufferOrder = append(a.BufferOrder, name) } -func (adapter *MysqlAdapter) stringyType(ctype string) bool { +func (a *MysqlAdapter) stringyType(ctype string) bool { ctype = strings.ToLower(ctype) return ctype == "varchar" || ctype == "tinytext" || ctype == "text" || ctype == "mediumtext" || ctype == "longtext" || ctype == "char" || ctype == "datetime" || ctype == "timestamp" || ctype == "time" || ctype == "date" } diff --git a/query_gen/pgsql.go b/query_gen/pgsql.go index 1eecbd3e..98222cf0 100644 --- a/query_gen/pgsql.go +++ b/query_gen/pgsql.go @@ -195,23 +195,23 @@ func (a *PgsqlAdapter) SimpleInsert(name string, table string, columns string, f return q, nil } -func (a *PgsqlAdapter) buildColumns(columns string) (q string) { - if columns == "" { +func (a *PgsqlAdapter) buildColumns(cols string) (q string) { + if cols == "" { return "" } // Escape the column names, just in case we've used a reserved keyword - for _, column := range processColumns(columns) { - if column.Type == "function" { - q += column.Left + "," + for _, col := range processColumns(cols) { + if col.Type == TokenFunc { + q += col.Left + "," } else { - q += "\"" + column.Left + "\"," + q += "\"" + col.Left + "\"," } } return q[0 : len(q)-1] } // TODO: Implement this -func (a *PgsqlAdapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) { +func (a *PgsqlAdapter) SimpleReplace(name, table, columns, fields string) (string, error) { if table == "" { return "", errors.New("You need a name for this table") } @@ -249,26 +249,25 @@ func (a *PgsqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { q := "UPDATE \"" + up.table + "\" SET " for _, item := range processSet(up.set) { - q += "`" + item.Column + "` =" + q += "`" + item.Column + "`=" for _, token := range item.Expr { switch token.Type { - case "function": + case TokenFunc: // TODO: Write a more sophisticated function parser on the utils side. if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "LOCALTIMESTAMP()" } q += " " + token.Contents - case "operator", "number", "substitute", "or": + case TokenOp, TokenNumber, TokenSub, TokenOr: q += " " + token.Contents - case "column": + case TokenColumn: q += " `" + token.Contents + "`" - case "string": + case TokenString: q += " '" + token.Contents + "'" } } q += "," } - // Remove the trailing comma q = q[0 : len(q)-1] // Add support for BETWEEN x.x @@ -277,17 +276,17 @@ func (a *PgsqlAdapter) SimpleUpdate(up *updatePrebuilder) (string, error) { for _, loc := range processWhere(up.where) { for _, token := range loc.Expr { switch token.Type { - case "function": + case TokenFunc: // TODO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity? if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "LOCALTIMESTAMP()" } q += " " + token.Contents - case "operator", "number", "substitute", "or": + case TokenOp, TokenNumber, TokenSub, TokenOr, TokenNot, TokenLike: q += " " + token.Contents - case "column": + case TokenColumn: q += " `" + token.Contents + "`" - case "string": + case TokenString: q += " '" + token.Contents + "'" default: panic("This token doesn't exist o_o") diff --git a/query_gen/querygen.go b/query_gen/querygen.go index 11052ae9..71fd4c20 100644 --- a/query_gen/querygen.go +++ b/query_gen/querygen.go @@ -25,7 +25,7 @@ type DBTableKey struct { Type string // Foreign keys only - FTable string + FTable string Cascade bool } @@ -57,7 +57,8 @@ type DBColumn 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 string // function or column + Type int } type DBField struct { @@ -82,9 +83,22 @@ type DBOrder struct { Order string } +const ( + TokenFunc = iota + TokenOp + TokenColumn + TokenNumber + TokenString + TokenSub + TokenOr + TokenNot + TokenLike +) + type DBToken struct { Contents string - Type string // function, operator, column, number, string, substitute + //Type string // function, op, column, number, string, sub, not, like + Type int } type DBSetter struct { diff --git a/query_gen/utils.go b/query_gen/utils.go index e53af5ef..9e00a48d 100644 --- a/query_gen/utils.go +++ b/query_gen/utils.go @@ -13,18 +13,18 @@ import ( ) // TODO: Add support for numbers and strings? -func processColumns(colstr string) (columns []DBColumn) { - if colstr == "" { +func processColumns(colStr string) (columns []DBColumn) { + if colStr == "" { return columns } - colstr = strings.Replace(colstr, " as ", " AS ", -1) - for _, segment := range strings.Split(colstr, ",") { - var outcol DBColumn + colStr = strings.Replace(colStr, " as ", " AS ", -1) + for _, segment := range strings.Split(colStr, ",") { + var outCol DBColumn dotHalves := strings.Split(strings.TrimSpace(segment), ".") var halves []string if len(dotHalves) == 2 { - outcol.Table = dotHalves[0] + outCol.Table = dotHalves[0] halves = strings.Split(dotHalves[1], " AS ") } else { halves = strings.Split(dotHalves[0], " AS ") @@ -32,132 +32,132 @@ func processColumns(colstr string) (columns []DBColumn) { halves[0] = strings.TrimSpace(halves[0]) if len(halves) == 2 { - outcol.Alias = strings.TrimSpace(halves[1]) + outCol.Alias = strings.TrimSpace(halves[1]) } if halves[0][len(halves[0])-1] == ')' { - outcol.Type = "function" + outCol.Type = TokenFunc } else if halves[0] == "?" { - outcol.Type = "substitute" + outCol.Type = TokenSub } else { - outcol.Type = "column" + outCol.Type = TokenColumn } - outcol.Left = halves[0] - columns = append(columns, outcol) + outCol.Left = halves[0] + columns = append(columns, outCol) } return columns } // TODO: Allow order by statements without a direction -func processOrderby(orderstr string) (order []DBOrder) { - if orderstr == "" { +func processOrderby(orderStr string) (order []DBOrder) { + if orderStr == "" { return order } - for _, segment := range strings.Split(orderstr, ",") { - var outorder DBOrder + for _, segment := range strings.Split(orderStr, ",") { + var outOrder DBOrder halves := strings.Split(strings.TrimSpace(segment), " ") if len(halves) != 2 { continue } - outorder.Column = halves[0] - outorder.Order = strings.ToLower(halves[1]) - order = append(order, outorder) + outOrder.Column = halves[0] + outOrder.Order = strings.ToLower(halves[1]) + order = append(order, outOrder) } return order } -func processJoiner(joinstr string) (joiner []DBJoiner) { - if joinstr == "" { +func processJoiner(joinStr string) (joiner []DBJoiner) { + if joinStr == "" { return joiner } - joinstr = strings.Replace(joinstr, " on ", " ON ", -1) - joinstr = strings.Replace(joinstr, " and ", " AND ", -1) - for _, segment := range strings.Split(joinstr, " AND ") { - var outjoin DBJoiner + joinStr = strings.Replace(joinStr, " on ", " ON ", -1) + joinStr = strings.Replace(joinStr, " and ", " AND ", -1) + for _, segment := range strings.Split(joinStr, " AND ") { + var outJoin DBJoiner var parseOffset int var left, right string left, parseOffset = getIdentifier(segment, parseOffset) - outjoin.Operator, parseOffset = getOperator(segment, parseOffset+1) + outJoin.Operator, parseOffset = getOperator(segment, parseOffset+1) right, parseOffset = getIdentifier(segment, parseOffset+1) leftColumn := strings.Split(left, ".") rightColumn := strings.Split(right, ".") - outjoin.LeftTable = strings.TrimSpace(leftColumn[0]) - outjoin.RightTable = strings.TrimSpace(rightColumn[0]) - outjoin.LeftColumn = strings.TrimSpace(leftColumn[1]) - outjoin.RightColumn = strings.TrimSpace(rightColumn[1]) + outJoin.LeftTable = strings.TrimSpace(leftColumn[0]) + outJoin.RightTable = strings.TrimSpace(rightColumn[0]) + outJoin.LeftColumn = strings.TrimSpace(leftColumn[1]) + outJoin.RightColumn = strings.TrimSpace(rightColumn[1]) - joiner = append(joiner, outjoin) + joiner = append(joiner, outJoin) } return joiner } -func (where *DBWhere) parseNumber(segment string, i int) int { +func (wh *DBWhere) parseNumber(segment string, i int) int { var buffer string for ; i < len(segment); i++ { - char := segment[i] - if '0' <= char && char <= '9' { - buffer += string(char) + ch := segment[i] + if '0' <= ch && ch <= '9' { + buffer += string(ch) } else { i-- - where.Expr = append(where.Expr, DBToken{buffer, "number"}) + wh.Expr = append(wh.Expr, DBToken{buffer, TokenNumber}) return i } } return i } -func (where *DBWhere) parseColumn(segment string, i int) int { +func (wh *DBWhere) parseColumn(segment string, i int) int { var buffer string for ; i < len(segment); i++ { - char := segment[i] + ch := segment[i] switch { - case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '.' || char == '_': - buffer += string(char) - case char == '(': - return where.parseFunction(segment, buffer, i) + case ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '.' || ch == '_': + buffer += string(ch) + case ch == '(': + return wh.parseFunction(segment, buffer, i) default: i-- - where.Expr = append(where.Expr, DBToken{buffer, "column"}) + wh.Expr = append(wh.Expr, DBToken{buffer, TokenColumn}) return i } } return i } -func (where *DBWhere) parseFunction(segment string, buffer string, i int) int { - var preI = i +func (wh *DBWhere) parseFunction(segment string, buffer string, i int) int { + preI := i i = skipFunctionCall(segment, i-1) buffer += segment[preI:i] + string(segment[i]) - where.Expr = append(where.Expr, DBToken{buffer, "function"}) + wh.Expr = append(wh.Expr, DBToken{buffer, TokenFunc}) return i } -func (where *DBWhere) parseString(segment string, i int) int { +func (wh *DBWhere) parseString(segment string, i int) int { var buffer string i++ for ; i < len(segment); i++ { - char := segment[i] - if char != '\'' { - buffer += string(char) + ch := segment[i] + if ch != '\'' { + buffer += string(ch) } else { - where.Expr = append(where.Expr, DBToken{buffer, "string"}) + wh.Expr = append(wh.Expr, DBToken{buffer, TokenString}) return i } } return i } -func (where *DBWhere) parseOperator(segment string, i int) int { +func (wh *DBWhere) parseOperator(segment string, i int) int { var buffer string for ; i < len(segment); i++ { - char := segment[i] - if isOpByte(char) { - buffer += string(char) + ch := segment[i] + if isOpByte(ch) { + buffer += string(ch) } else { i-- - where.Expr = append(where.Expr, DBToken{buffer, "operator"}) + wh.Expr = append(wh.Expr, DBToken{buffer, TokenOp}) return i } } @@ -175,35 +175,41 @@ func normalizeOr(in string) string { } // TODO: Write tests for this -func processWhere(wherestr string) (where []DBWhere) { - if wherestr == "" { +func processWhere(whereStr string) (where []DBWhere) { + if whereStr == "" { return where } - wherestr = normalizeAnd(wherestr) - wherestr = normalizeOr(wherestr) + whereStr = normalizeAnd(whereStr) + whereStr = normalizeOr(whereStr) - for _, segment := range strings.Split(wherestr, " AND ") { - var tmpWhere = &DBWhere{[]DBToken{}} - segment += ")" - for i := 0; i < len(segment); i++ { - char := segment[i] + for _, seg := range strings.Split(whereStr, " AND ") { + tmpWhere := &DBWhere{[]DBToken{}} + seg += ")" + for i := 0; i < len(seg); i++ { + ch := seg[i] switch { - case '0' <= char && char <= '9': - i = tmpWhere.parseNumber(segment, i) + case '0' <= ch && ch <= '9': + i = tmpWhere.parseNumber(seg, i) // TODO: Sniff the third byte offset from char or it's non-existent to avoid matching uppercase strings which start with OR - case char == 'O' && (i+1) < len(segment) && segment[i+1] == 'R': - tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"OR", "or"}) + case ch == 'O' && (i+1) < len(seg) && seg[i+1] == 'R': + tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"OR", TokenOr}) i += 1 - case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_': - i = tmpWhere.parseColumn(segment, i) - case char == '\'': - i = tmpWhere.parseString(segment, i) - case char == ')' && i < (len(segment)-1): - tmpWhere.Expr = append(tmpWhere.Expr, DBToken{")", "operator"}) - case isOpByte(char): - i = tmpWhere.parseOperator(segment, i) - case char == '?': - tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"?", "substitute"}) + case ch == 'N' && (i+2) < len(seg) && seg[i+1] == 'O' && seg[i+2] == 'T': + tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"NOT", TokenNot}) + i += 2 + case ch == 'L' && (i+3) < len(seg) && seg[i+1] == 'I' && seg[i+2] == 'K' && seg[i+3] == 'E': + tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"LIKE", TokenLike}) + i += 3 + case ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_': + i = tmpWhere.parseColumn(seg, i) + case ch == '\'': + i = tmpWhere.parseString(seg, i) + case ch == ')' && i < (len(seg)-1): + tmpWhere.Expr = append(tmpWhere.Expr, DBToken{")", TokenOp}) + case isOpByte(ch): + i = tmpWhere.parseOperator(seg, i) + case ch == '?': + tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"?", TokenSub}) } } where = append(where, *tmpWhere) @@ -211,47 +217,47 @@ func processWhere(wherestr string) (where []DBWhere) { return where } -func (setter *DBSetter) parseNumber(segment string, i int) int { +func (set *DBSetter) parseNumber(segment string, i int) int { var buffer string for ; i < len(segment); i++ { - char := segment[i] - if '0' <= char && char <= '9' { - buffer += string(char) + ch := segment[i] + if '0' <= ch && ch <= '9' { + buffer += string(ch) } else { - setter.Expr = append(setter.Expr, DBToken{buffer, "number"}) + set.Expr = append(set.Expr, DBToken{buffer, TokenNumber}) return i } } return i } -func (setter *DBSetter) parseColumn(segment string, i int) int { +func (set *DBSetter) parseColumn(segment string, i int) int { var buffer string for ; i < len(segment); i++ { - char := segment[i] + ch := segment[i] switch { - case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_': - buffer += string(char) - case char == '(': - return setter.parseFunction(segment, buffer, i) + case ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_': + buffer += string(ch) + case ch == '(': + return set.parseFunction(segment, buffer, i) default: i-- - setter.Expr = append(setter.Expr, DBToken{buffer, "column"}) + set.Expr = append(set.Expr, DBToken{buffer, TokenColumn}) return i } } return i } -func (setter *DBSetter) parseFunction(segment string, buffer string, i int) int { - var preI = i +func (set *DBSetter) parseFunction(segment string, buffer string, i int) int { + preI := i i = skipFunctionCall(segment, i-1) buffer += segment[preI:i] + string(segment[i]) - setter.Expr = append(setter.Expr, DBToken{buffer, "function"}) + set.Expr = append(set.Expr, DBToken{buffer, TokenFunc}) return i } -func (setter *DBSetter) parseString(segment string, i int) int { +func (set *DBSetter) parseString(segment string, i int) int { var buffer string i++ for ; i < len(segment); i++ { @@ -259,22 +265,22 @@ func (setter *DBSetter) parseString(segment string, i int) int { if char != '\'' { buffer += string(char) } else { - setter.Expr = append(setter.Expr, DBToken{buffer, "string"}) + set.Expr = append(set.Expr, DBToken{buffer, TokenString}) return i } } return i } -func (setter *DBSetter) parseOperator(segment string, i int) int { +func (set *DBSetter) parseOperator(segment string, i int) int { var buffer string for ; i < len(segment); i++ { - char := segment[i] - if isOpByte(char) { - buffer += string(char) + ch := segment[i] + if isOpByte(ch) { + buffer += string(ch) } else { i-- - setter.Expr = append(setter.Expr, DBToken{buffer, "operator"}) + set.Expr = append(set.Expr, DBToken{buffer, TokenOp}) return i } } @@ -316,18 +322,18 @@ func processSet(setstr string) (setter []DBSetter) { segment := halves[1] + ")" for i := 0; i < len(segment); i++ { - char := segment[i] + ch := segment[i] switch { - case '0' <= char && char <= '9': + case '0' <= ch && ch <= '9': i = tmpSetter.parseNumber(segment, i) - case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_': + case ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_': i = tmpSetter.parseColumn(segment, i) - case char == '\'': + case ch == '\'': i = tmpSetter.parseString(segment, i) - case isOpByte(char): + case isOpByte(ch): i = tmpSetter.parseOperator(segment, i) - case char == '?': - tmpSetter.Expr = append(tmpSetter.Expr, DBToken{"?", "substitute"}) + case ch == '?': + tmpSetter.Expr = append(tmpSetter.Expr, DBToken{"?", TokenSub}) } } setter = append(setter, *tmpSetter) @@ -335,86 +341,88 @@ func processSet(setstr string) (setter []DBSetter) { return setter } -func processLimit(limitstr string) (limiter DBLimit) { - halves := strings.Split(limitstr, ",") +func processLimit(limitStr string) (limit DBLimit) { + halves := strings.Split(limitStr, ",") if len(halves) == 2 { - limiter.Offset = halves[0] - limiter.MaxCount = halves[1] + limit.Offset = halves[0] + limit.MaxCount = halves[1] } else { - limiter.MaxCount = halves[0] + limit.MaxCount = halves[0] } - return limiter + return limit } -func isOpByte(char byte) bool { - return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' || char == '(' || char == ')' +func isOpByte(ch byte) bool { + return ch == '<' || ch == '>' || ch == '=' || ch == '!' || ch == '*' || ch == '%' || ch == '+' || ch == '-' || ch == '/' || ch == '(' || ch == ')' } -func isOpRune(char rune) bool { - return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' || char == '(' || char == ')' +func isOpRune(ch rune) bool { + return ch == '<' || ch == '>' || ch == '=' || ch == '!' || ch == '*' || ch == '%' || ch == '+' || ch == '-' || ch == '/' || ch == '(' || ch == ')' } -func processFields(fieldstr string) (fields []DBField) { - if fieldstr == "" { +func processFields(fieldStr string) (fields []DBField) { + if fieldStr == "" { return fields } var buffer string var lastItem int - fieldstr += "," - for i := 0; i < len(fieldstr); i++ { - if fieldstr[i] == '(' { - i = skipFunctionCall(fieldstr, i-1) - fields = append(fields, DBField{Name: fieldstr[lastItem : i+1], Type: getIdentifierType(fieldstr[lastItem : i+1])}) + fieldStr += "," + for i := 0; i < len(fieldStr); i++ { + ch := fieldStr[i] + if ch == '(' { + i = skipFunctionCall(fieldStr, i-1) + fields = append(fields, DBField{Name: fieldStr[lastItem : i+1], Type: getIdentifierType(fieldStr[lastItem : i+1])}) buffer = "" lastItem = i + 2 - } else if fieldstr[i] == ',' && buffer != "" { + } else if ch == ',' && buffer != "" { fields = append(fields, DBField{Name: buffer, Type: getIdentifierType(buffer)}) buffer = "" lastItem = i + 1 - } else if (fieldstr[i] >= 32) && fieldstr[i] != ',' && fieldstr[i] != ')' { - buffer += string(fieldstr[i]) + } else if (ch >= 32) && ch != ',' && ch != ')' { + buffer += string(ch) } } return fields } -func getIdentifierType(identifier string) string { - if ('a' <= identifier[0] && identifier[0] <= 'z') || ('A' <= identifier[0] && identifier[0] <= 'Z') { - if identifier[len(identifier)-1] == ')' { +func getIdentifierType(iden string) string { + if ('a' <= iden[0] && iden[0] <= 'z') || ('A' <= iden[0] && iden[0] <= 'Z') { + if iden[len(iden)-1] == ')' { return "function" } return "column" } - if identifier[0] == '\'' || identifier[0] == '"' { + if iden[0] == '\'' || iden[0] == '"' { return "string" } return "literal" } -func getIdentifier(segment string, startOffset int) (out string, i int) { - segment = strings.TrimSpace(segment) - segment += " " // Avoid overflow bugs with slicing - for i = startOffset; i < len(segment); i++ { - if segment[i] == '(' { - i = skipFunctionCall(segment, i) - return strings.TrimSpace(segment[startOffset:i]), (i - 1) +func getIdentifier(seg string, startOffset int) (out string, i int) { + seg = strings.TrimSpace(seg) + seg += " " // Avoid overflow bugs with slicing + for i = startOffset; i < len(seg); i++ { + ch := seg[i] + if ch == '(' { + i = skipFunctionCall(seg, i) + return strings.TrimSpace(seg[startOffset:i]), (i - 1) } - if (segment[i] == ' ' || isOpByte(segment[i])) && i != startOffset { - return strings.TrimSpace(segment[startOffset:i]), (i - 1) + if (ch == ' ' || isOpByte(ch)) && i != startOffset { + return strings.TrimSpace(seg[startOffset:i]), (i - 1) } } - return strings.TrimSpace(segment[startOffset:]), (i - 1) + return strings.TrimSpace(seg[startOffset:]), (i - 1) } -func getOperator(segment string, startOffset int) (out string, i int) { - segment = strings.TrimSpace(segment) - segment += " " // Avoid overflow bugs with slicing - for i = startOffset; i < len(segment); i++ { - if !isOpByte(segment[i]) && i != startOffset { - return strings.TrimSpace(segment[startOffset:i]), (i - 1) +func getOperator(seg string, startOffset int) (out string, i int) { + seg = strings.TrimSpace(seg) + seg += " " // Avoid overflow bugs with slicing + for i = startOffset; i < len(seg); i++ { + if !isOpByte(seg[i]) && i != startOffset { + return strings.TrimSpace(seg[startOffset:i]), (i - 1) } } - return strings.TrimSpace(segment[startOffset:]), (i - 1) + return strings.TrimSpace(seg[startOffset:]), (i - 1) } func skipFunctionCall(data string, index int) int { diff --git a/routes.go b/routes.go index 697a62a4..bd55bc86 100644 --- a/routes.go +++ b/routes.go @@ -25,7 +25,7 @@ import ( // A blank list to fill out that parameter in Page for routes which don't use it var tList []interface{} -var successJSONBytes = []byte(`{"success":"1"}`) +var successJSONBytes = []byte(`{"success":1}`) // TODO: Refactor this // TODO: Use the phrase system @@ -115,8 +115,8 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError defer rows.Close() for rows.Next() { - var alert c.Alert - err = rows.Scan(&alert.ASID, &alert.ActorID, &alert.TargetUserID, &alert.Event, &alert.ElementType, &alert.ElementID, &createdAt) + var al c.Alert + err = rows.Scan(&al.ASID, &al.ActorID, &al.TargetUserID, &al.Event, &al.ElementType, &al.ElementID, &createdAt) if err != nil { return c.InternalErrorJS(err, w, r) } @@ -124,8 +124,8 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError uCreatedAt := createdAt.Unix() //log.Print("uCreatedAt", uCreatedAt) //if rCreatedAt == 0 || rCreatedAt < uCreatedAt { - alerts = append(alerts, alert) - actors = append(actors, alert.ActorID) + alerts = append(alerts, al) + actors = append(actors, al.ActorID) //} if uCreatedAt > topCreatedAt { topCreatedAt = uCreatedAt @@ -325,10 +325,10 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout func routeJSAntispam(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { h := sha256.New() h.Write([]byte(c.JSTokenBox.Load().(string))) - h.Write([]byte(user.LastIP)) + h.Write([]byte(user.GetIP())) jsToken := hex.EncodeToString(h.Sum(nil)) - var innerCode = "`document.getElementByld('golden-watch').value = '" + jsToken + "';`" + innerCode := "`document.getElementByld('golden-watch').value = '" + jsToken + "';`" io.WriteString(w, `let hihi = `+innerCode+`; hihi = hihi.replace('ld','Id'); eval(hihi);`) diff --git a/routes/account.go b/routes/account.go index 3f9044af..60243e2c 100644 --- a/routes/account.go +++ b/routes/account.go @@ -36,11 +36,11 @@ func AccountLoginSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.R return c.LocalError("You're already logged in.", w, r, user) } - username := c.SanitiseSingleLine(r.PostFormValue("username")) - uid, err, requiresExtraAuth := c.Auth.Authenticate(username, r.PostFormValue("password")) + name := c.SanitiseSingleLine(r.PostFormValue("username")) + uid, err, requiresExtraAuth := c.Auth.Authenticate(name, r.PostFormValue("password")) if err != nil { // TODO: uid is currently set to 0 as authenticate fetches the user by username and password. Get the actual uid, so we can alert the user of attempted logins? What if someone takes advantage of the response times to deduce if an account exists? - logItem := &c.LoginLogItem{UID: uid, Success: false, IP: user.LastIP} + logItem := &c.LoginLogItem{UID: uid, Success: false, IP: user.GetIP()} _, err := logItem.Create() if err != nil { return c.InternalError(err, w, r) @@ -49,7 +49,7 @@ func AccountLoginSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.R } // TODO: Take 2FA into account - logItem := &c.LoginLogItem{UID: uid, Success: true, IP: user.LastIP} + logItem := &c.LoginLogItem{UID: uid, Success: true, IP: user.GetIP()} _, err = logItem.Create() if err != nil { return c.InternalError(err, w, r) @@ -91,7 +91,7 @@ func loginSuccess(uid int, w http.ResponseWriter, r *http.Request, user *c.User) if user.IsAdmin { // Is this error check redundant? We already check for the error in PreRoute for the same IP // TODO: Should we be logging this? - log.Printf("#%d has logged in with IP %s", uid, user.LastIP) + log.Printf("#%d has logged in with IP %s", uid, user.GetIP()) } http.Redirect(w, r, "/", http.StatusSeeOther) return nil @@ -220,7 +220,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User) if !c.Config.DisableJSAntispam { h := sha256.New() h.Write([]byte(c.JSTokenBox.Load().(string))) - h.Write([]byte(user.LastIP)) + h.Write([]byte(user.GetIP())) if r.PostFormValue("golden-watch") != hex.EncodeToString(h.Sum(nil)) { regError(p.GetErrorPhrase("register_might_be_machine"), "js-antispam") } @@ -261,7 +261,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User) } } - regLog := c.RegLogItem{Username: name, Email: email, FailureReason: regErrReason, Success: regSuccess, IP: user.LastIP} + regLog := c.RegLogItem{Username: name, Email: email, FailureReason: regErrReason, Success: regSuccess, IP: user.GetIP()} _, err = regLog.Create() if err != nil { return c.InternalError(err, w, r) diff --git a/routes/panel/backups.go b/routes/panel/backups.go index f3946ce9..7f23fe6c 100644 --- a/routes/panel/backups.go +++ b/routes/panel/backups.go @@ -42,7 +42,7 @@ func Backups(w http.ResponseWriter, r *http.Request, user c.User, backupURL stri } // TODO: Fix the problem where non-existent files aren't greeted with custom 404s on ServeFile()'s side http.ServeFile(w, r, "./backups/"+backupURL) - err = c.AdminLogs.Create("download", 0, "backup", user.LastIP, user.ID) + err = c.AdminLogs.Create("download", 0, "backup", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/forums.go b/routes/panel/forums.go index 000c88d8..4df659dc 100644 --- a/routes/panel/forums.go +++ b/routes/panel/forums.go @@ -71,7 +71,7 @@ func ForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.R if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("create", fid, "forum", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", fid, "forum", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -130,7 +130,7 @@ func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, sfi } else if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("delete", fid, "forum", user.LastIP, user.ID) + err = c.AdminLogs.Create("delete", fid, "forum", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -161,7 +161,7 @@ func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro } c.Forums.UpdateOrder(updateMap) - err := c.AdminLogs.Create("reorder", 0, "forum", user.LastIP, user.ID) + err := c.AdminLogs.Create("reorder", 0, "forum", user.GetIP(), user.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -259,7 +259,7 @@ func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sfid if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", fid, "forum", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", fid, "forum", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -300,7 +300,7 @@ func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user c.User, if err != nil { return c.LocalErrorJSQ(err.Error(), w, r, user, js) } - err = c.AdminLogs.Create("edit", fid, "forum", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", fid, "forum", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -348,7 +348,6 @@ func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user c.User, } else if err != nil { return c.InternalError(err, w, r) } - if forum.Preset == "" { forum.Preset = "custom" } @@ -438,7 +437,7 @@ func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, user c if err != nil { return c.LocalErrorJSQ(err.Error(), w, r, user, js) } - err = c.AdminLogs.Create("edit", fid, "forum", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", fid, "forum", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/groups.go b/routes/panel/groups.go index e1709097..e7c2ee4f 100644 --- a/routes/panel/groups.go +++ b/routes/panel/groups.go @@ -229,7 +229,7 @@ func GroupsPromotionsCreateSubmit(w http.ResponseWriter, r *http.Request, user c if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("create", pid, "group_promotion", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", pid, "group_promotion", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -276,7 +276,7 @@ func GroupsPromotionsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("delete", pid, "group_promotion", user.LastIP, user.ID) + err = c.AdminLogs.Create("delete", pid, "group_promotion", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -375,7 +375,6 @@ func GroupsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sgid if err != nil { return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) } - group, err := c.Groups.Get(gid) if err == sql.ErrNoRows { //log.Print("aaaaa monsters") @@ -386,24 +385,25 @@ func GroupsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sgid return ferr } - gname := r.FormValue("name") - if gname == "" { + name := r.FormValue("name") + if name == "" { return c.LocalError(p.GetErrorPhrase("panel_groups_need_name"), w, r, user) } - gtag := r.FormValue("tag") + tag := r.FormValue("tag") rank := r.FormValue("type") var originalRank string // TODO: Use a switch for this - if group.IsAdmin { + switch { + case group.IsAdmin: originalRank = "Admin" - } else if group.IsMod { + case group.IsMod: originalRank = "Mod" - } else if group.IsBanned { + case group.IsBanned: originalRank = "Banned" - } else if group.ID == 6 { + case group.ID == 6: originalRank = "Guest" - } else { + default: originalRank = "Member" } @@ -436,11 +436,11 @@ func GroupsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sgid } } - err = group.Update(gname, gtag) + err = group.Update(name, tag) if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("edit", group.ID, "group", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", group.ID, "group", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -489,7 +489,7 @@ func GroupsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user c.User, if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("edit", group.ID, "group", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", group.ID, "group", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -536,7 +536,7 @@ func GroupsCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.R if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("create", gid, "group", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", gid, "group", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/pages.go b/routes/panel/pages.go index 454999b4..defdee68 100644 --- a/routes/panel/pages.go +++ b/routes/panel/pages.go @@ -63,7 +63,7 @@ func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("create", pid, "page", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", pid, "page", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -130,7 +130,7 @@ func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid s if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("edit", pid, "page", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", pid, "page", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -153,7 +153,7 @@ func PagesDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("delete", pid, "page", user.LastIP, user.ID) + err = c.AdminLogs.Create("delete", pid, "page", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/plugins.go b/routes/panel/plugins.go index b386633f..a856dba1 100644 --- a/routes/panel/plugins.go +++ b/routes/panel/plugins.go @@ -73,7 +73,7 @@ func PluginsActivate(w http.ResponseWriter, r *http.Request, user c.User, uname if err != nil { return c.LocalError(err.Error(), w, r, user) } - err = c.AdminLogs.CreateExtra("activate", 0, "plugin", user.LastIP, user.ID, c.SanitiseSingleLine(plugin.Name)) + err = c.AdminLogs.CreateExtra("activate", 0, "plugin", user.GetIP(), user.ID, c.SanitiseSingleLine(plugin.Name)) if err != nil { return c.InternalError(err, w, r) } @@ -111,7 +111,7 @@ func PluginsDeactivate(w http.ResponseWriter, r *http.Request, user c.User, unam if plugin.Deactivate != nil { plugin.Deactivate(plugin) } - err = c.AdminLogs.CreateExtra("deactivate", 0, "plugin", user.LastIP, user.ID, c.SanitiseSingleLine(plugin.Name)) + err = c.AdminLogs.CreateExtra("deactivate", 0, "plugin", user.GetIP(), user.ID, c.SanitiseSingleLine(plugin.Name)) if err != nil { return c.InternalError(err, w, r) } @@ -181,7 +181,7 @@ func PluginsInstall(w http.ResponseWriter, r *http.Request, user c.User, uname s if err != nil { return c.LocalError(err.Error(), w, r, user) } - err = c.AdminLogs.CreateExtra("install", 0, "plugin", user.LastIP, user.ID, c.SanitiseSingleLine(plugin.Name)) + err = c.AdminLogs.CreateExtra("install", 0, "plugin", user.GetIP(), user.ID, c.SanitiseSingleLine(plugin.Name)) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/settings.go b/routes/panel/settings.go index ac1fbd3f..b9e635c6 100644 --- a/routes/panel/settings.go +++ b/routes/panel/settings.go @@ -109,7 +109,7 @@ func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, name return rerr } // TODO: Avoid this hack - err := c.AdminLogs.Create(name, 0, "setting", user.LastIP, user.ID) + err := c.AdminLogs.Create(name, 0, "setting", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/themes.go b/routes/panel/themes.go index b1f20190..0e40cbac 100644 --- a/routes/panel/themes.go +++ b/routes/panel/themes.go @@ -58,7 +58,7 @@ func ThemesSetDefault(w http.ResponseWriter, r *http.Request, user c.User, uname if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.CreateExtra("set_default", 0, "theme", user.LastIP, user.ID, c.SanitiseSingleLine(theme.Name)) + err = c.AdminLogs.CreateExtra("set_default", 0, "theme", user.GetIP(), user.ID, c.SanitiseSingleLine(theme.Name)) if err != nil { return c.InternalError(err, w, r) } @@ -233,7 +233,7 @@ func ThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, user c.Use if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", menuItem.ID, "menu_item", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", menuItem.ID, "menu_item", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -266,7 +266,7 @@ func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, user c.U if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("create", itemID, "menu_item", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", itemID, "menu_item", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -300,7 +300,7 @@ func ThemesMenuItemDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.U if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("delete", menuItem.ID, "menu_item", user.LastIP, user.ID) + err = c.AdminLogs.Create("delete", menuItem.ID, "menu_item", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -342,7 +342,7 @@ func ThemesMenuItemOrderSubmit(w http.ResponseWriter, r *http.Request, user c.Us } menuHold.UpdateOrder(updateMap) - err = c.AdminLogs.Create("suborder", menuHold.MenuID, "menu", user.LastIP, user.ID) + err = c.AdminLogs.Create("suborder", menuHold.MenuID, "menu", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -446,7 +446,7 @@ func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", widget.ID, "widget", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", widget.ID, "widget", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -474,7 +474,7 @@ func ThemesWidgetsCreateSubmit(w http.ResponseWriter, r *http.Request, user c.Us if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("create", wid, "widget", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", wid, "widget", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -507,7 +507,7 @@ func ThemesWidgetsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.Us if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("delete", widget.ID, "widget", user.LastIP, user.ID) + err = c.AdminLogs.Create("delete", widget.ID, "widget", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/users.go b/routes/panel/users.go index 97a1d821..1eeb6019 100644 --- a/routes/panel/users.go +++ b/routes/panel/users.go @@ -153,7 +153,7 @@ func UsersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid s } targetUser.CacheRemove() - err = c.AdminLogs.Create("edit", targetUser.ID, "user", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", targetUser.ID, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -209,7 +209,7 @@ func UsersAvatarSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("edit", targetUser.ID, "user", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", targetUser.ID, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -249,7 +249,7 @@ func UsersAvatarRemoveSubmit(w http.ResponseWriter, r *http.Request, user c.User return ferr } - err = c.AdminLogs.Create("edit", targetUser.ID, "user", user.LastIP, user.ID) + err = c.AdminLogs.Create("edit", targetUser.ID, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/word_filters.go b/routes/panel/word_filters.go index c74a495f..b64fb8c4 100644 --- a/routes/panel/word_filters.go +++ b/routes/panel/word_filters.go @@ -52,7 +52,7 @@ func WordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("create", wfid, "word_filter", user.LastIP, user.ID) + err = c.AdminLogs.Create("create", wfid, "word_filter", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -111,7 +111,7 @@ func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.CreateExtra("edit", wfid, "word_filter", user.LastIP, user.ID, string(lBytes)) + err = c.AdminLogs.CreateExtra("edit", wfid, "word_filter", user.GetIP(), user.ID, string(lBytes)) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -125,7 +125,7 @@ func WordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User if ferr != nil { return ferr } - js := (r.PostFormValue("js") == "1") + js := r.PostFormValue("js") == "1" if !user.Perms.EditSettings { return c.NoPermissionsJSQ(w, r, user, js) } @@ -138,7 +138,7 @@ func WordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User if err == sql.ErrNoRows { return c.LocalErrorJSQ("This word filter doesn't exist", w, r, user, js) } - err = c.AdminLogs.Create("delete", wfid, "word_filter", user.LastIP, user.ID) + err = c.AdminLogs.Create("delete", wfid, "word_filter", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/poll.go b/routes/poll.go index 60385ad3..54090ddd 100644 --- a/routes/poll.go +++ b/routes/poll.go @@ -57,7 +57,7 @@ func PollVote(w http.ResponseWriter, r *http.Request, user c.User, sPollID strin if err != nil { return c.LocalError("Malformed input", w, r, user) } - err = poll.CastVote(optionIndex, user.ID, user.LastIP) + err = poll.CastVote(optionIndex, user.ID, user.GetIP()) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/profile_reply.go b/routes/profile_reply.go index a4595194..40cbf2b2 100644 --- a/routes/profile_reply.go +++ b/routes/profile_reply.go @@ -39,7 +39,7 @@ func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user c.Use return c.LocalError("You can't make a blank post", w, r, user) } // TODO: Fully parse the post and store it in the parsed column - _, err = c.Prstore.Create(profileOwner.ID, content, user.ID, user.LastIP) + _, err = c.Prstore.Create(profileOwner.ID, content, user.ID, user.GetIP()) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/reply.go b/routes/reply.go index a6ba7a8c..6dd35237 100644 --- a/routes/reply.go +++ b/routes/reply.go @@ -66,7 +66,7 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro content := c.PreparseMessage(r.PostFormValue("content")) // TODO: Fully parse the post and put that in the parsed column - rid, err := c.Rstore.Create(topic, content, user.LastIP, user.ID) + rid, err := c.Rstore.Create(topic, content, user.GetIP(), user.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -337,7 +337,7 @@ func ReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, srid return c.InternalErrorJSQ(err, w, r, js) } - err = c.ModLogs.Create("delete", reply.ParentID, "reply", user.LastIP, user.ID) + err = c.ModLogs.Create("delete", reply.ParentID, "reply", user.GetIP(), user.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } diff --git a/routes/topic.go b/routes/topic.go index 8588b087..b847e844 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -9,7 +9,6 @@ import ( "io" //"fmt" - "golang.org/x/image/tiff" "image" "image/gif" "image/jpeg" @@ -21,6 +20,8 @@ import ( "strconv" "strings" + "golang.org/x/image/tiff" + c "github.com/Azareal/Gosora/common" "github.com/Azareal/Gosora/common/counters" "github.com/Azareal/Gosora/common/phrases" @@ -332,7 +333,7 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c. } func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { - fid, err := strconv.Atoi(r.PostFormValue("topic-board")) + fid, err := strconv.Atoi(r.PostFormValue("board")) if err != nil { return c.LocalError(phrases.GetErrorPhrase("id_must_be_integer"), w, r, user) } @@ -345,10 +346,10 @@ func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro return c.NoPermissions(w, r, user) } - tname := c.SanitiseSingleLine(r.PostFormValue("topic-name")) - content := c.PreparseMessage(r.PostFormValue("topic-content")) + tname := c.SanitiseSingleLine(r.PostFormValue("name")) + content := c.PreparseMessage(r.PostFormValue("content")) // TODO: Fully parse the post and store it in the parsed column - tid, err := c.Topics.Create(fid, tname, content, user.ID, user.LastIP) + tid, err := c.Topics.Create(fid, tname, content, user.ID, user.GetIP()) if err != nil { switch err { case c.ErrNoRows: @@ -632,7 +633,7 @@ func DeleteTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro if err != nil { return c.PreError("The provided TopicID is not a valid number.", w, r) } - tids = append(tids, tid) + tids = []int{tid} } if len(tids) == 0 { return c.LocalErrorJSQ("You haven't provided any IDs", w, r, user, js) @@ -663,13 +664,13 @@ func DeleteTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro return c.InternalErrorJSQ(err, w, r, js) } - err = c.ModLogs.Create("delete", tid, "topic", user.LastIP, user.ID) + err = c.ModLogs.Create("delete", tid, "topic", user.GetIP(), user.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } // ? - We might need to add soft-delete before we can do an action reply for this - /*_, err = stmts.createActionReply.Exec(tid,"delete",ipaddress,user.ID) + /*_, err = stmts.createActionReply.Exec(tid,"delete",ip,user.ID) if err != nil { return c.InternalErrorJSQ(err,w,r,js) }*/ @@ -890,11 +891,11 @@ func MoveTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, sfid s } func addTopicAction(action string, t *c.Topic, u c.User) error { - err := c.ModLogs.Create(action, t.ID, "topic", u.LastIP, u.ID) + err := c.ModLogs.Create(action, t.ID, "topic", u.GetIP(), u.ID) if err != nil { return err } - return t.CreateActionReply(action, u.LastIP, u.ID) + return t.CreateActionReply(action, u.GetIP(), u.ID) } // TODO: Refactor this diff --git a/routes/user.go b/routes/user.go index 22526d31..f38e06e6 100644 --- a/routes/user.go +++ b/routes/user.go @@ -73,7 +73,7 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid str return c.InternalError(err, w, r) } - err = c.ModLogs.Create("ban", uid, "user", user.LastIP, user.ID) + err = c.ModLogs.Create("ban", uid, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -119,7 +119,7 @@ func UnbanUser(w http.ResponseWriter, r *http.Request, user c.User, suid string) return c.InternalError(err, w, r) } - err = c.ModLogs.Create("unban", uid, "user", user.LastIP, user.ID) + err = c.ModLogs.Create("unban", uid, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } @@ -160,7 +160,7 @@ func ActivateUser(w http.ResponseWriter, r *http.Request, user c.User, suid stri return c.InternalError(err, w, r) } - err = c.ModLogs.Create("activate", targetUser.ID, "user", user.LastIP, user.ID) + err = c.ModLogs.Create("activate", targetUser.ID, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/templates/create_topic.html b/templates/create_topic.html index b1a4bd44..5fcd6211 100644 --- a/templates/create_topic.html +++ b/templates/create_topic.html @@ -7,22 +7,22 @@
-
{{range .ItemList}}{{.Name}}{{end}}
-
+
-
+
{{if .CurrentUser.Perms.UploadFiles}} - + {{end}}
diff --git a/templates/forum.html b/templates/forum.html index 4538b575..1f3ba49c 100644 --- a/templates/forum.html +++ b/templates/forum.html @@ -31,12 +31,12 @@
{{lang - +
- +
diff --git a/templates/forum_gallery.html b/templates/forum_gallery.html index 138f9aaa..fb36bbb8 100644 --- a/templates/forum_gallery.html +++ b/templates/forum_gallery.html @@ -29,12 +29,12 @@
{{lang - +
- +
@@ -43,7 +43,7 @@
{{end}} {{end}} -
+
{{range .ItemList}}
diff --git a/templates/panel_themes_widgets_widget.html b/templates/panel_themes_widgets_widget.html index be9155eb..261a1f33 100644 --- a/templates/panel_themes_widgets_widget.html +++ b/templates/panel_themes_widgets_widget.html @@ -14,8 +14,8 @@
diff --git a/templates/topics.html b/templates/topics.html index 81ec81f3..0ad02b68 100644 --- a/templates/topics.html +++ b/templates/topics.html @@ -59,13 +59,13 @@
-
{{range .ForumList}}{{.Name}}{{end}}
- +
diff --git a/templates/topics_quick_topic.html b/templates/topics_quick_topic.html index 757f37fb..ba35e7d2 100644 --- a/templates/topics_quick_topic.html +++ b/templates/topics_quick_topic.html @@ -1,7 +1,7 @@
- +
diff --git a/tickloop.go b/tickloop.go index 40e987a4..b8205308 100644 --- a/tickloop.go +++ b/tickloop.go @@ -141,22 +141,34 @@ func tickLoop(thumbChan chan bool) { } } -func dailies() { +func asmMatches() { // TODO: Find a more efficient way of doing this - err := qgen.NewAcc().Select("activity_stream").Cols("asid").EachInt(func(asid int) error { - count, err := qgen.NewAcc().Count("activity_stream_matches").Where("asid = " + strconv.Itoa(asid)).Total() + acc := qgen.NewAcc() + countStmt := acc.Count("activity_stream_matches").Where("asid=?").Prepare() + if err := acc.FirstError(); err != nil { + c.LogError(err) + return + } + + err := acc.Select("activity_stream").Cols("asid").EachInt(func(asid int) error { + var count int + err := countStmt.QueryRow(asid).Scan(&count) if err != sql.ErrNoRows { return err } if count > 0 { return nil } - _, err = qgen.NewAcc().Delete("activity_stream").Where("asid = ?").Run(asid) + _, err = qgen.NewAcc().Delete("activity_stream").Where("asid=?").Run(asid) return err }) if err != nil && err != sql.ErrNoRows { c.LogError(err) } +} + +func dailies() { + asmMatches() if c.Config.LogPruneCutoff > -1 { f := func(tbl string) { @@ -172,7 +184,7 @@ func dailies() { if c.Config.PostIPCutoff > -1 { // TODO: Use unixtime to remove this MySQLesque logic? f := func(tbl string) { - _, err := qgen.NewAcc().Update(tbl).Set("ipaddress = '0'").DateOlderThan("createdAt",c.Config.PostIPCutoff,"day").Where("ipaddress != '0'").Exec() + _, err := qgen.NewAcc().Update(tbl).Set("ipaddress='0'").DateOlderThan("createdAt",c.Config.PostIPCutoff,"day").Where("ipaddress!='0'").Exec() if err != nil { c.LogError(err) } @@ -182,16 +194,30 @@ func dailies() { f("users_replies") // TODO: Find some way of purging the ip data in polls_votes without breaking any anti-cheat measures which might be running... maybe hash it instead? + } - // TODO: lastActiveAt isn't currently set, so we can't rely on this to purge last_ips of users who haven't been on in a while - /*_, err = qgen.NewAcc().Update("users").Set("last_ip = '0'").DateOlderThan("lastActiveAt",c.Config.PostIPCutoff,"day").Where("last_ip != '0'").Exec() + // TODO: lastActiveAt isn't currently set, so we can't rely on this to purge last_ips of users who haven't been on in a while + /*if c.Config.LastIPCutoff == -1 { + _, err := qgen.NewAcc().Update("users").Set("last_ip=0").Where("last_ip!=0").Exec() + if err != nil { + c.LogError(err) + } + } else */if c.Config.LastIPCutoff > 0 { + /*_, err = qgen.NewAcc().Update("users").Set("last_ip='0'").DateOlderThan("lastActiveAt",c.Config.PostIPCutoff,"day").Where("last_ip!='0'").Exec() if err != nil { c.LogError(err) }*/ + mon := time.Now().Month() + _, err := qgen.NewAcc().Update("users").Set("last_ip=0").Where("last_ip!=0 AND last_ip NOT LIKE '"+strconv.Itoa(int(mon))+"-%'").Exec() + if err != nil { + c.LogError(err) + } } - err = c.Meta.Set("lastDaily", strconv.FormatInt(time.Now().Unix(), 10)) - if err != nil { - c.LogError(err) + { + err := c.Meta.Set("lastDaily", strconv.FormatInt(time.Now().Unix(), 10)) + if err != nil { + c.LogError(err) + } } } \ No newline at end of file