Optimise the database layer.
Refactor database adapters. Experimental last ip cutoff. More parser test cases.
This commit is contained in:
parent
d22021b022
commit
35ddc89009
@ -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(),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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("</b>")
|
||||
closeStrike := []byte("</s>")
|
||||
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("</b>")
|
||||
closeStrike := []byte("</s>")
|
||||
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, "<a href='$1$2//$3' rel='ugc'>$1$2//$3</i>")
|
||||
msg = bbcodeURLLabel.ReplaceAllString(msg, "<a href='$1$2//$3' rel='ugc'>$4</i>")
|
||||
msg = bbcodeSpoiler.ReplaceAllString(msg, "<spoiler>$1</spoiler>")
|
||||
msg = bbcodeQuotes.ReplaceAllString(msg, "<blockquote>$1</blockquote>")
|
||||
return bbcodeCode.ReplaceAllString(msg, "<span class='codequotes'>$1</span>")
|
||||
}
|
||||
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("</b>")
|
||||
closeStrike := []byte("</s>")
|
||||
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, "<spoiler>$1</spoiler>")
|
||||
msg = bbcodeH1.ReplaceAllString(msg, "<h2>$1</h2>")
|
||||
} 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
|
||||
|
12
gen_mssql.go
12
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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -167,7 +167,14 @@ func TestParser(t *testing.T) {
|
||||
l.Add("// t", "// t")
|
||||
l.Add("http:// t", "<red>[Invalid URL]</red> 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:")
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
(() => {
|
||||
addInitHook("end_init", () => {
|
||||
$("#dash_username input").click(function(){
|
||||
$("#dash_username input").click(()=>{
|
||||
$("#dash_username button").show();
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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({
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
14
routes.go
14
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);`)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -7,22 +7,22 @@
|
||||
<form id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/?s={{.CurrentUser.Session}}" method="post"></form>
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem formlabel"><a>{{lang "create_topic_board"}}</a></div>
|
||||
<div class="formitem"><select form="quick_post_form" id="topic_board_input" name="topic-board">
|
||||
<div class="formitem"><select form="quick_post_form" id="topic_board_input" name="board">
|
||||
{{range .ItemList}}<option{{if eq .ID $.FID}} selected{{end}} value="{{.ID}}">{{.Name}}</option>{{end}}
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "create_topic_name"}}</a></div>
|
||||
<div class="formitem"><input form="quick_post_form" name="topic-name" type="text" placeholder="{{lang "create_topic_name"}}" required /></div>
|
||||
<div class="formitem"><input form="quick_post_form" name="name" type="text" placeholder="{{lang "create_topic_name"}}" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "create_topic_content"}}</a></div>
|
||||
<div class="formitem"><textarea form="quick_post_form" class="large" id="topic_content" name="topic-content" placeholder="{{lang "create_topic_placeholder"}}" required></textarea></div>
|
||||
<div class="formitem"><textarea form="quick_post_form" class="large" id="topic_content" name="content" placeholder="{{lang "create_topic_placeholder"}}" required></textarea></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<button form="quick_post_form" name="topic-button" class="formbutton">{{lang "create_topic_create_button"}}</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="quick_topic_upload_files" form="quick_post_form" id="quick_topic_upload_files" multiple type="file" style="display: none;" />
|
||||
<input name="quick_topic_upload_files" form="quick_post_form" id="quick_topic_upload_files" multiple type="file" style="display:none;" />
|
||||
<label for="quick_topic_upload_files" class="formbutton add_file_button">{{lang "create_topic_add_file_button"}}</label>{{end}}
|
||||
<div id="upload_file_dock"></div>
|
||||
</div>
|
||||
|
@ -31,12 +31,12 @@
|
||||
<div id="forum_topic_create_form" class="rowblock topic_create_form quick_create_form auto_hide" aria-label="{{lang "quick_topic.aria"}}">
|
||||
<form id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/?s={{.CurrentUser.Session}}" method="post"></form>
|
||||
<img class="little_row_avatar" src="{{.CurrentUser.MicroAvatar}}" height=64 alt="{{lang "quick_topic.avatar_alt"}}" title="{{lang "quick_topic.avatar_tooltip"}}" />
|
||||
<input form="quick_post_form" id="topic_board_input" name="topic-board" value="{{.Forum.ID}}" type="hidden">
|
||||
<input form="quick_post_form" id="topic_board_input" name="board" value="{{.Forum.ID}}" type="hidden">
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
<div class="formrow topic_name_row real_first_child">
|
||||
<div class="formitem">
|
||||
<input form="quick_post_form" name="topic-name" placeholder="{{lang "quick_topic.whatsup"}}" required>
|
||||
<input form="quick_post_form" name="name" placeholder="{{lang "quick_topic.whatsup"}}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,12 +29,12 @@
|
||||
<div id="forum_topic_create_form" class="rowblock topic_create_form quick_create_form auto_hide" aria-label="{{lang "quick_topic.aria"}}">
|
||||
<form id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/?s={{.CurrentUser.Session}}" method="post"></form>
|
||||
<img class="little_row_avatar" src="{{.CurrentUser.MicroAvatar}}" height=64 alt="{{lang "quick_topic.avatar_alt"}}" title="{{lang "quick_topic.avatar_tooltip"}}" />
|
||||
<input form="quick_post_form" id="topic_board_input" name="topic-board" value="{{.Forum.ID}}" type="hidden">
|
||||
<input form="quick_post_form" id="topic_board_input" name="board" value="{{.Forum.ID}}" type="hidden">
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
<div class="formrow topic_name_row real_first_child">
|
||||
<div class="formitem">
|
||||
<input form="quick_post_form" name="topic-name" placeholder="{{lang "quick_topic.whatsup"}}" required>
|
||||
<input form="quick_post_form" name="name" placeholder="{{lang "quick_topic.whatsup"}}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div id="forum_topic_list" class="rowblock micro_grid" aria-label="{{lang "forum_list_aria"}}" style="grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));">
|
||||
<div id="forum_topic_list" class="rowblock micro_grid" aria-label="{{lang "forum_list_aria"}}" style="grid-template-columns:repeat(auto-fit,minmax(130px,1fr));">
|
||||
{{range .ItemList}}<div class="rowitem" data-tid="{{.ID}}">
|
||||
<div>
|
||||
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement"><img src="{{.Content}}" style="width:100%;height:160px;" /></a>
|
||||
|
@ -14,8 +14,8 @@
|
||||
<div class="formitem formlabel"><a>{{lang "panel_themes_widgets_enabled"}}</a></div>
|
||||
<div class="formitem">
|
||||
<select name="wenabled">
|
||||
<option{{if .Enabled}} selected{{end}} value="1">{{lang "option_yes"}}</option>
|
||||
<option{{if not .Enabled}} selected{{end}} value="0">{{lang "option_no"}}</option>
|
||||
<option{{if .Enabled}} selected{{end}} value=1>{{lang "option_yes"}}</option>
|
||||
<option{{if not .Enabled}} selected{{end}} value=0>{{lang "option_no"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,13 +59,13 @@
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
<div class="formrow topic_board_row real_first_child">
|
||||
<div class="formitem"><select form="quick_post_form" id="topic_board_input" name="topic-board">
|
||||
<div class="formitem"><select form="quick_post_form" id="topic_board_input" name="board">
|
||||
{{range .ForumList}}<option{{if eq .ID $.DefaultForum}} selected{{end}} value="{{.ID}}">{{.Name}}</option>{{end}}
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="formrow topic_name_row">
|
||||
<div class="formitem">
|
||||
<input form="quick_post_form" name="topic-name" placeholder="{{lang "quick_topic.whatsup"}}" required>
|
||||
<input form="quick_post_form" name="name" placeholder="{{lang "quick_topic.whatsup"}}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<input form="quick_post_form" id="has_poll_input" name="has_poll" value=0 type="hidden" />
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="quick_post_form" id="input_content" name="topic-content" placeholder="{{lang "quick_topic.content_placeholder"}}" required></textarea>
|
||||
<textarea form="quick_post_form" id="input_content" name="content" placeholder="{{lang "quick_topic.content_placeholder"}}" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow poll_content_row auto_hide">
|
||||
|
46
tickloop.go
46
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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user