Skip to content

Commit

Permalink
CC database - new functions and improvements (#697)
Browse files Browse the repository at this point in the history
* CC database - extra functions and improvements

added dbDelMultiple , dbRank functions.
dbCount improvement.

* add queryFromArg function and shift UserCacheDel to end

* small fixes

* return pointer to Query

* Update tmplextensions.go

* Manually construct query instead of Marshal + Unmarshal

* Add missing bracket

* fix Interface typecasting

* toInt64 -> ToInt64

* remove unused package encoding/json

* Update tmplextensions.go
  • Loading branch information
Satty9361 authored Jul 2, 2021
1 parent f64fbce commit 3004660
Showing 1 changed file with 152 additions and 7 deletions.
159 changes: 152 additions & 7 deletions customcommands/tmplextensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ func init() {
ctx.ContextFuncs["dbDel"] = tmplDBDel(ctx)
ctx.ContextFuncs["dbDelById"] = tmplDBDelById(ctx)
ctx.ContextFuncs["dbDelByID"] = tmplDBDelById(ctx)
ctx.ContextFuncs["dbDelMultiple"] = tmplDBDelMultiple(ctx)
ctx.ContextFuncs["dbTopEntries"] = tmplDBTopEntries(ctx, false)
ctx.ContextFuncs["dbBottomEntries"] = tmplDBTopEntries(ctx, true)
ctx.ContextFuncs["dbCount"] = tmplDBCount(ctx)
ctx.ContextFuncs["dbRank"]= tmplDBRank(ctx)
})
}

Expand Down Expand Up @@ -334,6 +336,12 @@ func tmplCancelUniqueCC(ctx *templates.Context) interface{} {
}
}

type Query struct {
UserID null.Int64 `json:"user_id"`
Pattern null.String `json:"pattern"`
Reverse bool `json:"reverse"`
}

func tmplDBSet(ctx *templates.Context) interface{} {
return func(userID int64, key interface{}, value interface{}) (string, error) {
return (tmplDBSetExpire(ctx))(userID, key, value, -1)
Expand Down Expand Up @@ -518,6 +526,97 @@ func tmplDBDelById(ctx *templates.Context) interface{} {
return "", err
}
}
func tmplDBDelMultiple(ctx *templates.Context) interface{} {
return func(query interface{}, iAmount interface{}, iSkip interface{}) (interface{}, error) {
if ctx.IncreaseCheckCallCounterPremium("db_interactions", 10, 50) {
return "", templates.ErrTooManyCalls
}

if ctx.IncreaseCheckCallCounterPremium("db_multiple", 2, 10) {
return "", templates.ErrTooManyCalls
}

q, err := queryFromArg(query)
if err!= nil {
return "", err
}

amount := int(templates.ToInt64(iAmount))
if amount > 100 {
amount = 100
}
skip := int(templates.ToInt64(iSkip))
orderby := "value_num DESC, id DESC"
if q.Reverse {
orderby = "value_num ASC, id ASC"
}

qms := []qm.QueryMod{qm.Where("guild_id = ?", ctx.GS.ID), qm.OrderBy(orderby), qm.Limit(amount), qm.Offset(skip)}
if q.Pattern.Valid {
qms = append(qms, qm.Where("key LIKE ?", limitString(q.Pattern.String, 256)))
}
if q.UserID.Valid {
qms = append(qms, qm.Where("user_id = ?", q.UserID.Int64))
}
rows, err := models.TemplatesUserDatabases(qms...).AllG(context.Background())
if err != nil {
return "", err
}

cleared, err := rows.DeleteAllG(context.Background())
ctx.GS.UserCacheDel(CacheKeyDBLimits)
return cleared, err
}
}

func tmplDBRank(ctx *templates.Context) interface{} {
return func(query interface{}, userID int64, key string) (interface{}, error) {
if ctx.IncreaseCheckCallCounterPremium("db_interactions", 10, 50) {
return "", templates.ErrTooManyCalls
}

if ctx.IncreaseCheckCallCounterPremium("db_multiple", 2, 10) {
return "", templates.ErrTooManyCalls
}

q, err := queryFromArg(query)
if err!= nil {
return "", err
}

order := `DESC`
if q.Reverse {
order = `ASC`
}

if q.UserID.Valid && (q.UserID.Int64 != userID) { // some optimization
return 0, nil
}

const rawquery = `SELECT position FROM
(
SELECT user_id, key,
RANK() OVER
(
ORDER BY
CASE WHEN $1 = 'ASC' THEN value_num ELSE 0 END ASC,
CASE WHEN $1 = 'DESC' THEN value_num ELSE 0 END DESC,
CASE WHEN $1 = 'ASC' THEN id ELSE 0 END ASC,
CASE WHEN $1 = 'DESC' THEN id ELSE 0 END DESC
) AS position
FROM templates_user_database WHERE (guild_id = $2) AND ($3::bigint IS NULL OR user_id = $3) AND ($4::text IS NULL OR key LIKE $4) AND (expires_at IS NULL OR expires_at > now())
) AS w
WHERE user_id = $5 AND key = $6`

var rank int64
err = common.PQ.QueryRow(rawquery, order, ctx.GS.ID, q.UserID, q.Pattern, userID, key).Scan(&rank)
if err == sql.ErrNoRows {
return 0, nil
}
return rank, err

}
}

func tmplDBCount(ctx *templates.Context) interface{} {
return func(variadicArg ...interface{}) (interface{}, error) {
Expand All @@ -530,7 +629,7 @@ func tmplDBCount(ctx *templates.Context) interface{} {
}

var userID null.Int64
var key null.String
var pattern null.String
if len(variadicArg) > 0 {

switch arg := variadicArg[0].(type) {
Expand All @@ -541,23 +640,69 @@ func tmplDBCount(ctx *templates.Context) interface{} {
userID.Int64 = int64(arg)
userID.Valid = true
case string:
keyStr := limitString(arg, 256)
key.String = keyStr
key.Valid = true
patternStr := limitString(arg, 256)
pattern.String = patternStr
pattern.Valid = true
default:
return "", errors.New("Invalid Argument Data Type")
q, err := queryFromArg(arg)
if err!= nil {
return "", err
}
userID = q.UserID
pattern = q.Pattern

}

}

const q = `SELECT count(*) FROM templates_user_database WHERE (guild_id = $1) AND ($2::bigint IS NULL OR user_id = $2) AND ($3::text IS NULL OR key = $3) AND (expires_at IS NULL or expires_at > now())`
const q = `SELECT count(*) FROM templates_user_database WHERE (guild_id = $1) AND ($2::bigint IS NULL OR user_id = $2) AND ($3::text IS NULL OR key LIKE $3) AND (expires_at IS NULL or expires_at > now())`

var count int64
err := common.PQ.QueryRow(q, ctx.GS.ID, userID, key).Scan(&count)
err := common.PQ.QueryRow(q, ctx.GS.ID, userID, pattern).Scan(&count)
return count, err
}
}

func queryFromArg(query interface{}) (*Query, error) {

querySdict, err := templates.StringKeyDictionary(query)
if err != nil {
return nil, err
}

var q Query
for key, val := range querySdict {
switch key {
case "userID":
switch val.(type) {
case int, int64:
q.UserID.Int64 = templates.ToInt64(val)
q.UserID.Valid = true

default:
return &q, errors.New("Invalid UserID datatype in query. Must be a number")
}

case "pattern":
q.Pattern.String = limitString(templates.ToString(val), 256)
q.Pattern.Valid = true

case "reverse":
revFlag, ok := val.(bool)
if !ok {
return &q, errors.New("Invalid reverse flag datatype in query. Must be a boolean value.")
}
q.Reverse = revFlag

default:
return &q, errors.New("Invalid Key: " + key + " passed to query constructor")
}
}

return &q, nil
}


func tmplDBTopEntries(ctx *templates.Context, bottom bool) interface{} {
orderBy := "value_num DESC, id DESC"
if bottom {
Expand Down

0 comments on commit 3004660

Please sign in to comment.