Browse Source

update db

ls 8 months ago
parent
commit
3b94c008c5
12 changed files with 431 additions and 181 deletions
  1. 18 0
      bytes.go
  2. 68 0
      db/argument.go
  3. 9 79
      db/config.go
  4. 108 94
      db/db.go
  5. 31 0
      db/error.go
  6. 68 0
      db/isolation.go
  7. 24 0
      db/pool.go
  8. 75 0
      db/reply.go
  9. 1 0
      db/struct.go
  10. 16 0
      db/vars.go
  11. 5 2
      go.mod
  12. 8 6
      go.sum

+ 18 - 0
bytes.go

@@ -0,0 +1,18 @@
+package myth
+
+import "unsafe"
+
+// StringToBytes converts string to byte slice without a memory allocation.
+func StringToBytes(s string) []byte {
+	return *(*[]byte)(unsafe.Pointer(
+		&struct {
+			string
+			Cap int
+		}{s, len(s)},
+	))
+}
+
+// BytesToString converts byte slice to string without a memory allocation.
+func BytesToString(b []byte) string {
+	return *(*string)(unsafe.Pointer(&b))
+}

+ 68 - 0
db/argument.go

@@ -0,0 +1,68 @@
+package db
+
+import (
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	"strings"
+	"unsafe"
+)
+
+// M is a shortcut for map[string]any
+type M map[string]any
+
+// NewM new map[string]any
+func NewM() M {
+	return make(map[string]any)
+}
+
+// Set key-value to map[string]any
+func (m M) Set(k string, v any) {
+	m[k] = v
+}
+
+// Get value from map[string]interface{}
+func (m M) Get(k string) (v any, ok bool) {
+	v, ok = m[k]
+	return
+}
+
+// String map to json string
+func (m M) String() string {
+	bs, _ := json.Marshal(m)
+	return *(*string)(unsafe.Pointer(&bs))
+}
+
+// Bytes map to json bytes
+func (m M) Bytes() []byte {
+	bs, _ := json.Marshal(m)
+	return bs
+}
+
+// MarshalXML allows type Map to be used with xml.Marshal.
+func (m M) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	start.Name = xml.Name{
+		Space: "",
+		Local: "map",
+	}
+	if err := e.EncodeToken(start); err != nil {
+		return err
+	}
+	for key, value := range m {
+		elem := xml.StartElement{
+			Name: xml.Name{Space: "", Local: key},
+			Attr: []xml.Attr{},
+		}
+		if err := e.EncodeElement(value, elem); err != nil {
+			return err
+		}
+	}
+
+	return e.EncodeToken(xml.EndElement{Name: start.Name})
+}
+
+// Int64ArrayToIn array to db IN string
+func Int64ArrayToIn(arrs []int64) string {
+	ss := fmt.Sprint(arrs)
+	return strings.Replace(strings.Trim(fmt.Sprint(ss), "[]"), " ", ",", -1)
+}

+ 9 - 79
db/config.go

@@ -2,22 +2,6 @@ package db
 
 
 import (
 import (
 	"time"
 	"time"
-
-	"git.chuangxin1.com/cx/myth"
-)
-
-// DB error code
-const (
-	ErrOK                  = 0 // ErrOK ok
-	ErrException           = 1 // ErrException exception 异常
-	ErrExists              = 2 // ErrExists exists 数据存在
-	ErrNotFound            = 3 // ErrNotFound not found 未找到关键数据
-	ErrAuthorized          = 4 // ErrAuthorized authorized 未认证
-	ErrNotConnect          = 5 // ErrNotConnect connect error 数据库内部连接错误
-	ErrDataNotFound        = 6 // data not found 数据未找到
-	ErrExpired             = 7 // expired 已过期
-	ErrInsufficientBalance = 8 // insufficient balance 余额不足
-	ErrCreditLimit         = 9 // Credit Limit 超额
 )
 )
 
 
 // Config config
 // Config config
@@ -30,69 +14,15 @@ type Config struct {
 	MaxLifeTime  time.Duration
 	MaxLifeTime  time.Duration
 }
 }
 
 
-// Reply db exec return insert/update/delete
-type Reply struct {
-	OK           bool
-	Err          error
-	LastErr      error
-	ErrCode      int
-	LastID       int64
-	RowsAffected int64
-}
-
-// ReplyOk exec ok
-func ReplyOk(rowsAffected, lastID int64) Reply {
-	var reply Reply
-
-	reply.OK = true
-	reply.ErrCode = 0
-	reply.LastID = lastID
-	reply.RowsAffected = rowsAffected
-
-	return reply
-}
-
-// ReplyFaild exec faild
-func ReplyFaild(errCode int, err, errText error) (reply Reply) {
-	reply.OK = false
-	reply.ErrCode = errCode
-	reply.LastID = -1
-	reply.RowsAffected = -1
-
-	reply.Err = err
-	reply.LastErr = errText
-
-	return
-}
+// SetConfig set
+func SetConfig(cfg Config) {
+	config.Driver = cfg.Driver
+	config.DNS = cfg.DNS
 
 
-// ReplyToReplyData db reply to response
-func ReplyToReplyData(reply Reply) *myth.ReplyData {
-	status := myth.ErrOk
-	if !reply.OK {
-		switch reply.ErrCode {
-		case ErrException:
-			status = myth.ErrException
-		case ErrExists:
-			status = myth.ErrDataExists
-		case ErrNotFound:
-			status = myth.ErrDataNotFound
-		case ErrDataNotFound:
-			status = myth.ErrDataNotFound
-		case ErrAuthorized:
-			status = myth.ErrUnAuthorized
-		case ErrNotConnect:
-			status = myth.ErrNotFound
-		case ErrExpired:
-			status = myth.ErrNotAllowed
-		case ErrInsufficientBalance:
-			status = myth.ErrNotAllowed
-		case ErrCreditLimit:
-			status = myth.ErrNotAllowed
-		default:
-			status = myth.ErrException
-		}
-		return myth.ReplyErr(status, reply.LastErr.Error())
-	}
+	config.MaxOpenConns = cfg.MaxOpenConns
+	config.MaxIdle = cfg.MaxIdle
+	config.MaxIdleTime = cfg.MaxIdleTime * time.Second
+	config.MaxLifeTime = cfg.MaxLifeTime * time.Second
 
 
-	return myth.ReplyOk()
+	defaultDB = &DB{Driver: config.Driver}
 }
 }

+ 108 - 94
db/db.go

@@ -5,8 +5,6 @@ import (
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"sync"
-	"time"
 
 
 	// PostgreSQL
 	// PostgreSQL
 	_ "github.com/lib/pq"
 	_ "github.com/lib/pq"
@@ -16,17 +14,6 @@ import (
 	_ "github.com/mattn/go-sqlite3"
 	_ "github.com/mattn/go-sqlite3"
 )
 )
 
 
-var (
-	config Config
-	db     *sqlx.DB
-	//err    error
-	once sync.Once
-
-	defaultDB *DB
-
-	errNoneConnect = errors.New(`数据库连接错误`)
-)
-
 // DB define
 // DB define
 type DB struct {
 type DB struct {
 	Driver string
 	Driver string
@@ -39,18 +26,6 @@ type Tx struct {
 	tx *sqlx.Tx
 	tx *sqlx.Tx
 }
 }
 
 
-// SetConfig set
-func SetConfig(cfg Config) {
-	config.Driver = cfg.Driver
-	config.DNS = cfg.DNS
-	config.MaxOpenConns = cfg.MaxOpenConns
-	config.MaxIdle = cfg.MaxIdle
-	config.MaxIdleTime = cfg.MaxIdleTime * time.Second
-	config.MaxLifeTime = cfg.MaxLifeTime * time.Second
-
-	defaultDB = &DB{Driver: config.Driver}
-}
-
 // New new DB object
 // New new DB object
 func New() *DB {
 func New() *DB {
 	return &DB{Driver: config.Driver}
 	return &DB{Driver: config.Driver}
@@ -107,7 +82,7 @@ func connect() (dbx *sqlx.DB, err error) {
 			// */
 			// */
 	})
 	})
 	if db == nil {
 	if db == nil {
-		err = errNoneConnect
+		err = ErrNoneConnect
 		return
 		return
 	}
 	}
 	dbx = db
 	dbx = db
@@ -127,7 +102,7 @@ func connectContext(ctx context.Context) (dbx *sqlx.DB, err error) {
 		db.DB.SetConnMaxLifetime(config.MaxLifeTime)
 		db.DB.SetConnMaxLifetime(config.MaxLifeTime)
 	})
 	})
 	if db == nil {
 	if db == nil {
-		err = errNoneConnect
+		err = ErrNoneConnect
 		return
 		return
 	}
 	}
 	dbx = db
 	dbx = db
@@ -243,21 +218,21 @@ func (d *DB) Rollback() (err error) {
 }
 }
 
 
 // TransExec trans execute with named args
 // TransExec trans execute with named args
-func (d *DB) TransExec(query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) TransExec(query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.tx.NamedExec(query, args); err == nil {
 	if rs, err = d.tx.NamedExec(query, args); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
-		LastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // TransExec trans execute with named args
 // TransExec trans execute with named args
-func (d *DB) TransExecContext(ctx context.Context, query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) TransExecContext(ctx context.Context, query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.tx.NamedExecContext(ctx, query, args); err == nil {
 	if rs, err = d.tx.NamedExecContext(ctx, query, args); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
-		LastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
 	}
 	}
 	return
 	return
 }
 }
@@ -280,7 +255,7 @@ func (d *DB) TransUpdate(query string, args interface{}) (reply Reply) {
 
 
 // TransRow trans get row
 // TransRow trans get row
 func (d *DB) TransRow(dest interface{}, query string, args interface{}) (err error) {
 func (d *DB) TransRow(dest interface{}, query string, args interface{}) (err error) {
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.tx.PrepareNamed(query)
 	nstmt, err = d.tx.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -324,7 +299,7 @@ func (d *DB) Query(dest interface{}, query string, args interface{}) (err error)
 	}
 	}
 	defer d.Close()
 	defer d.Close()
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.conn.PrepareNamed(query)
 	nstmt, err = d.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -343,7 +318,7 @@ func (d *DB) QueryContext(ctx context.Context, dest interface{}, query string, a
 	}
 	}
 	defer d.Close()
 	defer d.Close()
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = d.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -362,7 +337,7 @@ func (d *DB) Rows(dest interface{}, query string, args interface{}) (err error)
 	}
 	}
 	defer d.Close()
 	defer d.Close()
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.conn.PrepareNamed(query)
 	nstmt, err = d.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -381,7 +356,7 @@ func (d *DB) RowsContext(ctx context.Context, dest interface{}, query string, ar
 	}
 	}
 	defer d.Close()
 	defer d.Close()
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = d.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -448,7 +423,7 @@ func (d *DB) Row(dest interface{}, query string, args interface{}) (err error) {
 	}
 	}
 	defer d.Close()
 	defer d.Close()
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.conn.PrepareNamed(query)
 	nstmt, err = d.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -467,7 +442,7 @@ func (d *DB) RowContext(ctx context.Context, dest interface{}, query string, arg
 	}
 	}
 	defer d.Close()
 	defer d.Close()
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = d.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = d.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -478,6 +453,21 @@ func (d *DB) RowContext(ctx context.Context, dest interface{}, query string, arg
 	return
 	return
 }
 }
 
 
+/*
+// In expands slice values in args, returning the modified query string and a new arg list that can be executed by a database. The `query` should use the `?` bindVar. The return value uses the `?` bindVar.
+func (d *DB) In(query string, args ...interface{}) (q string, params []interface{}, err error) {
+	err = d.Connect()
+	if err != nil {
+		return
+	}
+	defer d.Close()
+	var s string
+	s, params, err = sqlx.In(query, args)
+	q = d.conn.Rebind(s)
+	return
+}
+//*/
+
 // InsertReply insert and return DbReply
 // InsertReply insert and return DbReply
 func (d *DB) InsertReply(query string, args interface{}) (reply Reply) {
 func (d *DB) InsertReply(query string, args interface{}) (reply Reply) {
 	var (
 	var (
@@ -524,7 +514,7 @@ func (d *DB) UpdateReply(query string, args interface{}) (reply Reply) {
 }
 }
 
 
 // Insert insert into with named args
 // Insert insert into with named args
-func (d *DB) Insert(query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) Insert(query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	err = d.Connect()
 	err = d.Connect()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -533,14 +523,14 @@ func (d *DB) Insert(query string, args interface{}) (LastInsertId, RowsAffected
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.NamedExec(query, args); err == nil {
 	if rs, err = d.conn.NamedExec(query, args); err == nil {
-		LastInsertId, _ = rs.LastInsertId()
-		RowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // InsertContext insert into with named args
 // InsertContext insert into with named args
-func (d *DB) InsertContext(ctx context.Context, query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) InsertContext(ctx context.Context, query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	err = d.ConnectContext(ctx)
 	err = d.ConnectContext(ctx)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -549,14 +539,14 @@ func (d *DB) InsertContext(ctx context.Context, query string, args interface{})
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil {
 	if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil {
-		LastInsertId, _ = rs.LastInsertId()
-		RowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // Update update/delete with named args
 // Update update/delete with named args
-func (d *DB) Update(query string, args interface{}) (RowsAffected int64, err error) {
+func (d *DB) Update(query string, args interface{}) (rowsAffected int64, err error) {
 	err = d.Connect()
 	err = d.Connect()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -565,13 +555,13 @@ func (d *DB) Update(query string, args interface{}) (RowsAffected int64, err err
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.NamedExec(query, args); err == nil {
 	if rs, err = d.conn.NamedExec(query, args); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // Update update/delete with named args
 // Update update/delete with named args
-func (d *DB) UpdateContext(ctx context.Context, query string, args interface{}) (RowsAffected int64, err error) {
+func (d *DB) UpdateContext(ctx context.Context, query string, args interface{}) (rowsAffected int64, err error) {
 	err = d.ConnectContext(ctx)
 	err = d.ConnectContext(ctx)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -580,13 +570,13 @@ func (d *DB) UpdateContext(ctx context.Context, query string, args interface{})
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil {
 	if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // Exec exec
 // Exec exec
-func (d *DB) Exec(query string, args ...interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) Exec(query string, args ...interface{}) (lastInsertId, rowsAffected int64, err error) {
 	err = d.Connect()
 	err = d.Connect()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -595,14 +585,14 @@ func (d *DB) Exec(query string, args ...interface{}) (LastInsertId, RowsAffected
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.Exec(query, args...); err == nil {
 	if rs, err = d.conn.Exec(query, args...); err == nil {
-		LastInsertId, _ = rs.LastInsertId()
-		RowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // ExecContext exec
 // ExecContext exec
-func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (lastInsertId, rowsAffected int64, err error) {
 	err = d.ConnectContext(ctx)
 	err = d.ConnectContext(ctx)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -611,14 +601,14 @@ func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{})
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.ExecContext(ctx, query, args...); err == nil {
 	if rs, err = d.conn.ExecContext(ctx, query, args...); err == nil {
-		LastInsertId, _ = rs.LastInsertId()
-		RowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // Exec exec, with named args
 // Exec exec, with named args
-func (d *DB) NamedExec(query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) NamedExec(query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	err = d.Connect()
 	err = d.Connect()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -627,14 +617,14 @@ func (d *DB) NamedExec(query string, args interface{}) (LastInsertId, RowsAffect
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.NamedExec(query, args); err == nil {
 	if rs, err = d.conn.NamedExec(query, args); err == nil {
-		LastInsertId, _ = rs.LastInsertId()
-		RowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // NamedExecContext exec, with named args
 // NamedExecContext exec, with named args
-func (d *DB) NamedExecContext(ctx context.Context, query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (d *DB) NamedExecContext(ctx context.Context, query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	err = d.ConnectContext(ctx)
 	err = d.ConnectContext(ctx)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -643,8 +633,8 @@ func (d *DB) NamedExecContext(ctx context.Context, query string, args interface{
 
 
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil {
 	if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil {
-		LastInsertId, _ = rs.LastInsertId()
-		RowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
 	}
 	}
 	return
 	return
 }
 }
@@ -670,48 +660,48 @@ func (t *Tx) Rollback() error {
 }
 }
 
 
 // NamedExec executes a query that doesn't return rows. For example: an INSERT and UPDATE. with named args
 // NamedExec executes a query that doesn't return rows. For example: an INSERT and UPDATE. with named args
-func (t *Tx) NamedExec(query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (t *Tx) NamedExec(query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = t.tx.NamedExec(query, args); err == nil {
 	if rs, err = t.tx.NamedExec(query, args); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
-		LastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // NamedExecContext executes a query that doesn't return rows. For example: an INSERT and UPDATE. with named args
 // NamedExecContext executes a query that doesn't return rows. For example: an INSERT and UPDATE. with named args
-func (t *Tx) NamedExecContext(ctx context.Context, query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (t *Tx) NamedExecContext(ctx context.Context, query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = t.tx.NamedExecContext(ctx, query, args); err == nil {
 	if rs, err = t.tx.NamedExecContext(ctx, query, args); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
-		LastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // Exec executes a query that doesn't return rows. For example: an INSERT and UPDATE.
 // Exec executes a query that doesn't return rows. For example: an INSERT and UPDATE.
-func (t *Tx) Exec(query string, args ...interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (t *Tx) Exec(query string, args ...interface{}) (lastInsertId, rowsAffected int64, err error) {
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = t.tx.Exec(query, args...); err == nil {
 	if rs, err = t.tx.Exec(query, args...); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
-		LastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // ExecContext executes a query that doesn't return rows. For example: an INSERT and UPDATE.
 // ExecContext executes a query that doesn't return rows. For example: an INSERT and UPDATE.
-func (t *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (LastInsertId, RowsAffected int64, err error) {
+func (t *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (lastInsertId, rowsAffected int64, err error) {
 	var rs sql.Result
 	var rs sql.Result
 	if rs, err = t.tx.ExecContext(ctx, query, args...); err == nil {
 	if rs, err = t.tx.ExecContext(ctx, query, args...); err == nil {
-		RowsAffected, _ = rs.RowsAffected()
-		LastInsertId, _ = rs.LastInsertId()
+		rowsAffected, _ = rs.RowsAffected()
+		lastInsertId, _ = rs.LastInsertId()
 	}
 	}
 	return
 	return
 }
 }
 
 
 // Query executes a query that returns rows, typically a SELECT. with named args
 // Query executes a query that returns rows, typically a SELECT. with named args
 func (t *Tx) Query(dest interface{}, query string, args interface{}) (err error) {
 func (t *Tx) Query(dest interface{}, query string, args interface{}) (err error) {
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = t.tx.PrepareNamed(query)
 	nstmt, err = t.tx.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -724,7 +714,7 @@ func (t *Tx) Query(dest interface{}, query string, args interface{}) (err error)
 
 
 // QueryContext executes a query that returns rows, typically a SELECT. with named args
 // QueryContext executes a query that returns rows, typically a SELECT. with named args
 func (t *Tx) QueryContext(ctx context.Context, dest interface{}, query string, args interface{}) (err error) {
 func (t *Tx) QueryContext(ctx context.Context, dest interface{}, query string, args interface{}) (err error) {
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = t.tx.PrepareNamedContext(ctx, query)
 	nstmt, err = t.tx.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -737,7 +727,7 @@ func (t *Tx) QueryContext(ctx context.Context, dest interface{}, query string, a
 
 
 // QueryRow executes a query that returns rows, typically a SELECT. with named args
 // QueryRow executes a query that returns rows, typically a SELECT. with named args
 func (t *Tx) QueryRow(dest interface{}, query string, args interface{}) (err error) {
 func (t *Tx) QueryRow(dest interface{}, query string, args interface{}) (err error) {
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = t.tx.PrepareNamed(query)
 	nstmt, err = t.tx.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -750,7 +740,7 @@ func (t *Tx) QueryRow(dest interface{}, query string, args interface{}) (err err
 
 
 // QueryRowContext get row with named args
 // QueryRowContext get row with named args
 func (t *Tx) QueryRowContext(ctx context.Context, dest interface{}, query string, args interface{}) (err error) {
 func (t *Tx) QueryRowContext(ctx context.Context, dest interface{}, query string, args interface{}) (err error) {
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = t.tx.PrepareNamedContext(ctx, query)
 	nstmt, err = t.tx.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -761,6 +751,17 @@ func (t *Tx) QueryRowContext(ctx context.Context, dest interface{}, query string
 	return
 	return
 }
 }
 
 
+// Stats returns database statistics.
+func Stats() (s sql.DBStats, err error) {
+	defaultDB.conn, err = connect()
+	if err != nil {
+		return
+	}
+
+	s = defaultDB.Stats()
+	return
+}
+
 // Ping ping connect
 // Ping ping connect
 func Ping() (err error) {
 func Ping() (err error) {
 	defaultDB.conn, err = connect()
 	defaultDB.conn, err = connect()
@@ -801,7 +802,7 @@ func Query(dest interface{}, query string, args interface{}) (err error) {
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -819,7 +820,7 @@ func QueryContext(ctx context.Context, dest interface{}, query string, args inte
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -837,7 +838,7 @@ func Rows(dest interface{}, query string, args interface{}) (err error) {
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -855,7 +856,7 @@ func RowsContext(ctx context.Context, dest interface{}, query string, args inter
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -884,7 +885,7 @@ func QueryRow(dest interface{}, query string, args interface{}) (err error) {
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -902,7 +903,7 @@ func QueryRowContext(ctx context.Context, dest interface{}, query string, args i
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -920,7 +921,7 @@ func Row(dest interface{}, query string, args interface{}) (err error) {
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	nstmt, err = defaultDB.conn.PrepareNamed(query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -938,7 +939,7 @@ func RowContext(ctx context.Context, dest interface{}, query string, args interf
 		return
 		return
 	}
 	}
 
 
-	nstmt := new(sqlx.NamedStmt)
+	nstmt := &sqlx.NamedStmt{}
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -949,46 +950,59 @@ func RowContext(ctx context.Context, dest interface{}, query string, args interf
 	return
 	return
 }
 }
 
 
+/*
+// In expands slice values in args, returning the modified query string and a new arg list that can be executed by a database. The `query` should use the `?` bindVar. The return value uses the `?` bindVar.
+func In(query string, args ...interface{}) (q string, params []interface{}, err error) {
+	defaultDB.conn, err = connect()
+	if err != nil {
+		return
+	}
+
+	q, params, err = defaultDB.In(query, args...)
+	return
+}
+// */
+
 // Exec exec
 // Exec exec
-func Exec(query string, args ...interface{}) (LastInsertId, RowsAffected int64, err error) {
+func Exec(query string, args ...interface{}) (lastInsertId, rowsAffected int64, err error) {
 	defaultDB.conn, err = connect()
 	defaultDB.conn, err = connect()
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
 
 
-	LastInsertId, RowsAffected, err = defaultDB.Exec(query, args...)
+	lastInsertId, rowsAffected, err = defaultDB.Exec(query, args...)
 	return
 	return
 }
 }
 
 
 // Exec exec
 // Exec exec
-func ExecContext(ctx context.Context, query string, args ...interface{}) (LastInsertId, RowsAffected int64, err error) {
+func ExecContext(ctx context.Context, query string, args ...interface{}) (lastInsertId, rowsAffected int64, err error) {
 	defaultDB.conn, err = connectContext(ctx)
 	defaultDB.conn, err = connectContext(ctx)
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
 
 
-	LastInsertId, RowsAffected, err = defaultDB.ExecContext(ctx, query, args...)
+	lastInsertId, rowsAffected, err = defaultDB.ExecContext(ctx, query, args...)
 	return
 	return
 }
 }
 
 
 // NamedExec exec with named args
 // NamedExec exec with named args
-func NamedExec(query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func NamedExec(query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	defaultDB.conn, err = connect()
 	defaultDB.conn, err = connect()
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
 
 
-	LastInsertId, RowsAffected, err = defaultDB.NamedExec(query, args)
+	lastInsertId, rowsAffected, err = defaultDB.NamedExec(query, args)
 	return
 	return
 }
 }
 
 
 // NamedExecContext exec with named args
 // NamedExecContext exec with named args
-func NamedExecContext(ctx context.Context, query string, args interface{}) (LastInsertId, RowsAffected int64, err error) {
+func NamedExecContext(ctx context.Context, query string, args interface{}) (lastInsertId, rowsAffected int64, err error) {
 	defaultDB.conn, err = connectContext(ctx)
 	defaultDB.conn, err = connectContext(ctx)
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
 
 
-	LastInsertId, RowsAffected, err = defaultDB.NamedExecContext(ctx, query, args)
+	lastInsertId, rowsAffected, err = defaultDB.NamedExecContext(ctx, query, args)
 	return
 	return
 }
 }

+ 31 - 0
db/error.go

@@ -0,0 +1,31 @@
+package db
+
+import "errors"
+
+var (
+	ErrNoneConnect = errors.New(`not connect to database`)
+)
+
+// DB error code
+const (
+	// ErrOK ok
+	ErrOK = 0
+	// ErrException exception 异常
+	ErrException = 1
+	// ErrExists exists 数据存在
+	ErrExists = 2
+	// ErrNotFound not found 未找到关键数据
+	ErrNotFound = 3
+	// ErrAuthorized authorized 未认证
+	ErrAuthorized = 4
+	// ErrNotConnect connect error 数据库内部连接错误
+	ErrNotConnect = 5
+	// data not found 数据未找到
+	ErrDataNotFound = 6
+	// expired 已过期
+	ErrExpired = 7
+	// insufficient balance 余额不足
+	ErrInsufficientBalance = 8
+	// Credit Limit 超额
+	ErrCreditLimit = 9
+)

+ 68 - 0
db/isolation.go

@@ -0,0 +1,68 @@
+// https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels
+
+// Isolation levels
+// Of the four ACID properties in a DBMS (Database Management System), the // isolation property is the one most often relaxed. When attempting to maintain the highest level of isolation, a DBMS usually acquires locks on data which may result in a loss of concurrency, or implements multiversion concurrency control. This requires adding logic for the application to function correctly.
+// Most DBMSs offer a number of transaction isolation levels, which control the degree of locking that occurs when selecting data. For many database applications, the majority of database transactions can be constructed to avoid requiring high isolation levels (e.g. SERIALIZABLE level), thus reducing the locking overhead for the system. The programmer must carefully analyze database access code to ensure that any relaxation of isolation does not cause software bugs that are difficult to find. Conversely, if higher isolation levels are used, the possibility of deadlock is increased, which also requires careful analysis and programming techniques to avoid.
+// Since each isolation level is stronger than those below, in that no higher isolation level allows an action forbidden by a lower one, the standard permits a DBMS to run a transaction at an isolation level stronger than that requested (e.g., a "Read committed" transaction may actually be performed at a "Repeatable read" isolation level).
+// The isolation levels defined by the ANSI/ISO SQL standard are listed as follows.
+
+// Serializable
+// This is the highest isolation level.
+// With a lock-based concurrency control DBMS implementation, serializability requires read and write locks (acquired on selected data) to be released at the end of the transaction. Also range-locks must be acquired when a SELECT query uses a ranged WHERE clause, especially to avoid the phantom reads phenomenon.
+// When using non-lock based concurrency control, no locks are acquired; however, if the system detects a write collision among several concurrent transactions, only one of them is allowed to commit. See snapshot isolation for more details on this topic.
+// From : (Second Informal Review Draft) ISO/IEC 9075:1992, Database Language SQL- July 30, 1992: The execution of concurrent SQL-transactions at isolation level SERIALIZABLE is guaranteed to be serializable. A serializable execution is defined to be an execution of the operations of concurrently executing SQL-transactions that produces the same effect as some serial execution of those same SQL-transactions. A serial execution is one in which each SQL-transaction executes to completion before the next SQL-transaction begins.
+
+// Repeatable reads
+// In this isolation level, a lock-based concurrency control DBMS implementation keeps read and write locks (acquired on selected data) until the end of the transaction. However, range-locks are not managed, so phantom reads can occur.
+// Write skew is possible at this isolation level in some systems. Write skew is a phenomenon where two writes are allowed to the same column(s) in a table by two different writers (who have previously read the columns they are updating), resulting in the column having data that is a mix of the two transactions.[3][4]
+
+// Read committed
+// In this isolation level, a lock-based concurrency control DBMS implementation keeps write locks (acquired on selected data) until the end of the transaction, but read locks are released as soon as the SELECT operation is performed (so the non-repeatable reads phenomenon can occur in this isolation level). As in the previous level, range-locks are not managed.
+// Putting it in simpler words, read committed is an isolation level that guarantees that any data read is committed at the moment it is read. It simply restricts the reader from seeing any intermediate, uncommitted, 'dirty' read. It makes no promise whatsoever that if the transaction re-issues the read, it will find the same data; data is free to change after it is read.
+
+// Read uncommitted
+// This is the lowest isolation level. In this level, dirty reads are allowed, so one transaction may see not-yet-committed changes made by other transactions.
+
+package db
+
+import "database/sql"
+
+// IsolationDefault
+func IsolationDefault(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelDefault}
+}
+
+// IsolationReadUncommitted
+func IsolationReadUncommitted(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelReadUncommitted}
+}
+
+// IsolationReadCommitted
+func IsolationReadCommitted(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelReadCommitted}
+}
+
+// IsolationWriteCommitted
+func IsolationWriteCommitted(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelWriteCommitted}
+}
+
+// IsolationRepeatableRead
+func IsolationRepeatableRead(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelRepeatableRead}
+}
+
+// IsolationSnapshot
+func IsolationSnapshot(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelSnapshot}
+}
+
+// IsolationSerializable
+func IsolationSerializable(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelSerializable}
+}
+
+// IsolationLinearizable
+func IsolationLinearizable(readOnly bool) *sql.TxOptions {
+	return &sql.TxOptions{ReadOnly: readOnly, Isolation: sql.LevelLinearizable}
+}

+ 24 - 0
db/pool.go

@@ -0,0 +1,24 @@
+package db
+
+import (
+	"sync"
+
+	"github.com/jmoiron/sqlx"
+)
+
+// *sqlx.NamedStmt pool
+var namedStmtPool = sync.Pool{
+	New: func() any {
+		return &sqlx.NamedStmt{}
+	},
+}
+
+// NewNamedStmt alloc *sqlx.NamedStmt from pool
+func NewNamedStmt() *sqlx.NamedStmt {
+	return namedStmtPool.Get().(*sqlx.NamedStmt)
+}
+
+// PutNamedStmt release *sqlx.NamedStmt to pool
+func PutNamedStmt(stmt *sqlx.NamedStmt) {
+	namedStmtPool.Put(stmt)
+}

+ 75 - 0
db/reply.go

@@ -0,0 +1,75 @@
+package db
+
+import "git.chuangxin1.com/cx/myth"
+
+// Reply db exec return insert/update/delete
+type Reply struct {
+	OK           bool
+	Err          error
+	LastErr      error
+	ErrCode      int
+	LastID       int64
+	RowsAffected int64
+}
+
+// Ok check reply true/false
+func (r Reply) Ok() bool {
+	return r.OK
+}
+
+// ReplyOk exec ok
+func ReplyOk(rowsAffected, lastID int64) Reply {
+	var reply Reply
+
+	reply.OK = true
+	reply.ErrCode = 0
+	reply.LastID = lastID
+	reply.RowsAffected = rowsAffected
+
+	return reply
+}
+
+// ReplyFaild exec faild
+func ReplyFaild(errCode int, err, errText error) (reply Reply) {
+	reply.OK = false
+	reply.ErrCode = errCode
+	reply.LastID = -1
+	reply.RowsAffected = -1
+
+	reply.Err = err
+	reply.LastErr = errText
+
+	return
+}
+
+// ReplyToReplyData db reply to response
+func ReplyToReplyData(reply Reply) *myth.ReplyData {
+	status := myth.ErrOk
+	if !reply.OK {
+		switch reply.ErrCode {
+		case ErrException:
+			status = myth.ErrException
+		case ErrExists:
+			status = myth.ErrDataExists
+		case ErrNotFound:
+			status = myth.ErrDataNotFound
+		case ErrDataNotFound:
+			status = myth.ErrDataNotFound
+		case ErrAuthorized:
+			status = myth.ErrUnAuthorized
+		case ErrNotConnect:
+			status = myth.ErrNotFound
+		case ErrExpired:
+			status = myth.ErrNotAllowed
+		case ErrInsufficientBalance:
+			status = myth.ErrNotAllowed
+		case ErrCreditLimit:
+			status = myth.ErrNotAllowed
+		default:
+			status = myth.ErrException
+		}
+		return myth.ReplyErr(status, reply.LastErr.Error())
+	}
+
+	return myth.ReplyOk()
+}

+ 1 - 0
db/struct.go

@@ -0,0 +1 @@
+package db

+ 16 - 0
db/vars.go

@@ -0,0 +1,16 @@
+package db
+
+import (
+	"sync"
+
+	"github.com/jmoiron/sqlx"
+)
+
+var (
+	config Config
+	db     *sqlx.DB
+	//err    error
+	once sync.Once
+
+	defaultDB *DB
+)

+ 5 - 2
go.mod

@@ -1,6 +1,6 @@
 module git.chuangxin1.com/cx/myth
 module git.chuangxin1.com/cx/myth
 
 
-go 1.17
+go 1.18
 
 
 require (
 require (
 	github.com/go-redis/redis/v7 v7.4.1
 	github.com/go-redis/redis/v7 v7.4.1
@@ -13,4 +13,7 @@ require (
 	gopkg.in/yaml.v2 v2.4.0
 	gopkg.in/yaml.v2 v2.4.0
 )
 )
 
 
-require filippo.io/edwards25519 v1.1.0 // indirect
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	google.golang.org/protobuf v1.33.0 // indirect
+)

+ 8 - 6
go.sum

@@ -1,19 +1,18 @@
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
-github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
 github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=
 github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=
 github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
 github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
-github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
 github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
 github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
 github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
@@ -26,7 +25,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
 github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
@@ -50,6 +48,11 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -58,7 +61,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=