mirror of
https://shylinux.com/x/ContextOS
synced 2025-04-25 16:58:06 +08:00
326 lines
12 KiB
Go
326 lines
12 KiB
Go
package mdb
|
||
|
||
import (
|
||
"contexts/ctx"
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
_ "github.com/go-sql-driver/mysql"
|
||
"strings"
|
||
)
|
||
|
||
type MDB struct {
|
||
*sql.DB
|
||
*ctx.Context
|
||
}
|
||
|
||
func (mdb *MDB) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server {
|
||
c.Caches = map[string]*ctx.Cache{
|
||
"database": &ctx.Cache{Name: "数据库", Value: m.Confx("database", arg, 0), Help: "数据库"},
|
||
"username": &ctx.Cache{Name: "账户", Value: m.Confx("username", arg, 1), Help: "账户"},
|
||
"password": &ctx.Cache{Name: "密码", Value: m.Confx("password", arg, 2), Help: "密码"},
|
||
"address": &ctx.Cache{Name: "服务地址", Value: m.Confx("address", arg, 3), Help: "服务地址"},
|
||
"protocol": &ctx.Cache{Name: "服务协议(tcp)", Value: m.Confx("protocol", arg, 4), Help: "服务协议"},
|
||
"driver": &ctx.Cache{Name: "数据库驱动(mysql)", Value: m.Confx("driver", arg, 5), Help: "数据库驱动"},
|
||
}
|
||
c.Configs = map[string]*ctx.Config{
|
||
"dbs": &ctx.Config{Name: "dbs", Value: []string{}, Help: "关系表"},
|
||
"tables": &ctx.Config{Name: "dbs", Value: []string{}, Help: "关系表"},
|
||
"table": &ctx.Config{Name: "关系表", Value: "0", Help: "关系表"},
|
||
"field": &ctx.Config{Name: "字段名", Value: "", Help: "字段名"},
|
||
"where": &ctx.Config{Name: "条件", Value: "", Help: "条件"},
|
||
"group": &ctx.Config{Name: "聚合", Value: "", Help: "聚合"},
|
||
"order": &ctx.Config{Name: "排序", Value: "", Help: "排序"},
|
||
"limit": &ctx.Config{Name: "分页", Value: "10", Help: "分页"},
|
||
"offset": &ctx.Config{Name: "偏移", Value: "0", Help: "偏移"},
|
||
"parse": &ctx.Config{Name: "解析", Value: "", Help: "解析"},
|
||
}
|
||
|
||
s := new(MDB)
|
||
s.Context = c
|
||
return s
|
||
}
|
||
func (mdb *MDB) Begin(m *ctx.Message, arg ...string) ctx.Server {
|
||
return mdb
|
||
}
|
||
func (mdb *MDB) Start(m *ctx.Message, arg ...string) bool {
|
||
db, e := sql.Open(m.Cap("driver"), fmt.Sprintf("%s:%s@%s(%s)/%s",
|
||
m.Cap("username"), m.Cap("password"), m.Cap("protocol"), m.Cap("address"), m.Cap("database")))
|
||
m.Assert(e)
|
||
mdb.DB = db
|
||
m.Log("info", "mdb open %s", m.Cap("database"))
|
||
return false
|
||
}
|
||
func (mdb *MDB) Close(m *ctx.Message, arg ...string) bool {
|
||
switch mdb.Context {
|
||
case m.Target():
|
||
case m.Source():
|
||
}
|
||
return false
|
||
}
|
||
|
||
var Index = &ctx.Context{Name: "mdb", Help: "数据中心",
|
||
Caches: map[string]*ctx.Cache{
|
||
"nsource": &ctx.Cache{Name: "数据源数量", Value: "0", Help: "已打开数据库的数量"},
|
||
},
|
||
Configs: map[string]*ctx.Config{
|
||
"database": &ctx.Config{Name: "默认数据库", Value: "demo", Help: "默认数据库"},
|
||
"username": &ctx.Config{Name: "默认用户名", Value: "demo", Help: "默认用户名"},
|
||
"password": &ctx.Config{Name: "默认密码", Value: "demo", Help: "默认密码"},
|
||
"protocol": &ctx.Config{Name: "默认协议", Value: "tcp", Help: "默认协议"},
|
||
"address": &ctx.Config{Name: "默认地址", Value: "", Help: "默认地址"},
|
||
"driver": &ctx.Config{Name: "数据库驱动(mysql)", Value: "mysql", Help: "数据库驱动"},
|
||
|
||
"csv_col_sep": &ctx.Config{Name: "字段分隔符", Value: "\t", Help: "字段分隔符"},
|
||
"csv_row_sep": &ctx.Config{Name: "记录分隔符", Value: "\n", Help: "记录分隔符"},
|
||
},
|
||
Commands: map[string]*ctx.Command{
|
||
"open": &ctx.Command{Name: "open [database [username [password [address [protocol [driver]]]]]]",
|
||
Help: "open打开数据库, database: 数据库名, username: 用户名, password: 密码, address: 服务地址, protocol: 服务协议, driver: 数据库类型",
|
||
Form: map[string]int{"dbname": 1, "dbhelp": 1},
|
||
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
dbname := fmt.Sprintf("db%d", m.Capi("nsource", 1))
|
||
dbhelp := "数据源"
|
||
if m.Has("dbname") {
|
||
dbname = m.Option("dbname")
|
||
}
|
||
if m.Has("dbhelp") {
|
||
dbname = m.Option("dbhelp")
|
||
}
|
||
m.Start(dbname, dbhelp, arg...)
|
||
}},
|
||
"exec": &ctx.Command{Name: "exec sql [arg]", Help: "操作数据库, sql: SQL语句, arg: 操作参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
if mdb, ok := m.Target().Server.(*MDB); m.Assert(ok) && mdb.DB != nil {
|
||
which := make([]interface{}, 0, len(arg))
|
||
for _, v := range arg[1:] {
|
||
which = append(which, v)
|
||
}
|
||
|
||
ret, e := mdb.Exec(arg[0], which...)
|
||
m.Assert(e)
|
||
id, e := ret.LastInsertId()
|
||
m.Assert(e)
|
||
n, e := ret.RowsAffected()
|
||
m.Assert(e)
|
||
|
||
m.Log("info", "last(%s) nrow(%s)", m.Append("last", id), m.Append("nrow", n))
|
||
m.Echo("%d", n)
|
||
}
|
||
}},
|
||
"query": &ctx.Command{Name: "query sql [arg]", Help: "查询数据库, sql: SQL语句, arg: 查询参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
if mdb, ok := m.Target().Server.(*MDB); m.Assert(ok) && mdb.DB != nil {
|
||
which := make([]interface{}, 0, len(arg))
|
||
for _, v := range arg[1:] {
|
||
which = append(which, v)
|
||
}
|
||
|
||
rows, e := mdb.Query(arg[0], which...)
|
||
m.Assert(e)
|
||
defer rows.Close()
|
||
|
||
cols, e := rows.Columns()
|
||
m.Assert(e)
|
||
num := len(cols)
|
||
|
||
for rows.Next() {
|
||
vals := make([]interface{}, num)
|
||
ptrs := make([]interface{}, num)
|
||
for i := range vals {
|
||
ptrs[i] = &vals[i]
|
||
}
|
||
rows.Scan(ptrs...)
|
||
|
||
for i, k := range cols {
|
||
switch b := vals[i].(type) {
|
||
case nil:
|
||
m.Add("append", k, "")
|
||
case []byte:
|
||
m.Add("append", k, string(b))
|
||
case int64:
|
||
m.Add("append", k, fmt.Sprintf("%d", b))
|
||
default:
|
||
m.Add("append", k, fmt.Sprintf("%v", b))
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(m.Meta["append"]) > 0 {
|
||
m.Log("info", "rows(%d) cols(%d)", len(m.Meta[m.Meta["append"][0]]), len(m.Meta["append"]))
|
||
} else {
|
||
m.Log("info", "rows(0) cols(0)")
|
||
}
|
||
}
|
||
}},
|
||
"db": &ctx.Command{Name: "db [which]", Help: "查看或选择数据库信息", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
if mdb, ok := m.Target().Server.(*MDB); m.Assert(ok) && mdb.DB != nil {
|
||
if len(arg) == 0 {
|
||
msg := m.Spawn().Cmd("query", "show databases")
|
||
dbs := []string{}
|
||
for i, v := range msg.Meta[msg.Meta["append"][0]] {
|
||
dbs = append(dbs, v)
|
||
m.Echo("%d: %s\n", i, v)
|
||
}
|
||
m.Target().Configs["dbs"].Value = dbs
|
||
return
|
||
}
|
||
|
||
db := m.Confv("dbs", arg[0]).(string)
|
||
m.Assert(m.Spawn().Cmd("exec", fmt.Sprintf("use %s", db)))
|
||
m.Echo(m.Cap("database", db))
|
||
}
|
||
}},
|
||
"tab": &ctx.Command{Name: "tab[which [field]]", Help: "查看关系表信息,which: 表名, field: 字段名", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
if _, ok := m.Target().Server.(*MDB); m.Assert(ok) {
|
||
if len(arg) == 0 {
|
||
msg := m.Spawn().Cmd("query", "show tables")
|
||
tables := []string{}
|
||
for i, v := range msg.Meta[msg.Meta["append"][0]] {
|
||
tables = append(tables, v)
|
||
m.Echo("%d: %s\n", i, v)
|
||
}
|
||
m.Target().Configs["tables"].Value = tables
|
||
return
|
||
}
|
||
|
||
table := arg[0]
|
||
switch v := m.Confv("tables", arg[0]).(type) {
|
||
case string:
|
||
table = v
|
||
}
|
||
|
||
msg := m.Spawn().Cmd("query", fmt.Sprintf("desc %s", table))
|
||
m.Copy(msg, "append")
|
||
m.Table()
|
||
}
|
||
}},
|
||
"show": &ctx.Command{Name: "show table fields...",
|
||
Help: "查询数据库, table: 表名, fields: 字段, where: 查询条件, group: 聚合字段, order: 排序字段",
|
||
Form: map[string]int{"where": 1, "group": 1, "order": 1, "limit": 1, "offset": 1, "other": -1,
|
||
"extra_field": 2, "extra_fields": 1, "extra_format": 1, "trans_field": 1, "trans_map": 2},
|
||
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
if _, ok := m.Target().Server.(*MDB); m.Assert(ok) {
|
||
table := m.Confx("table", arg, 0)
|
||
if v := m.Confv("tables", table); v != nil {
|
||
table = v.(string)
|
||
}
|
||
|
||
fields := []string{"*"}
|
||
if len(arg) > 1 {
|
||
fields = arg[1:]
|
||
} else if m.Confs("field") {
|
||
fields = []string{m.Conf("field")}
|
||
}
|
||
field := strings.Join(fields, ",")
|
||
|
||
where := m.Confx("where", m.Option("where"), "where %s")
|
||
group := m.Confx("group", m.Option("group"), "group by %s")
|
||
order := m.Confx("order", m.Option("order"), "order by %s")
|
||
limit := m.Confx("limit", m.Option("limit"), "limit %s")
|
||
offset := m.Confx("offset", m.Option("offset"), "offset %s")
|
||
|
||
other := m.Meta["other"]
|
||
for i, v := range other {
|
||
if len(v) > 1 {
|
||
switch v[0] {
|
||
case '$':
|
||
other[i] = m.Cap(v[1:])
|
||
case '@':
|
||
other[i] = m.Conf(v[1:])
|
||
}
|
||
}
|
||
}
|
||
|
||
msg := m.Spawn().Cmd("query", fmt.Sprintf("select %s from %s %s %s %s %s %s", field, table, where, group, order, limit, offset), other)
|
||
m.Copy(msg, "append")
|
||
|
||
m.Target().Configs["template_value"] = &ctx.Config{}
|
||
if m.Has("extra_field") && len(m.Meta[m.Option("extra_field")]) > 0 {
|
||
format := "%v"
|
||
if m.Has("extra_format") {
|
||
format = m.Option("extra_format")
|
||
}
|
||
for i := 0; i < len(m.Meta[m.Option("extra_field")]); i++ {
|
||
json.Unmarshal([]byte(m.Meta[m.Option("extra_field")][i]), &m.Target().Configs["template_value"].Value)
|
||
if m.Meta["extra_field"][1] != "" {
|
||
m.Target().Configs["template_value"].Value = m.Confv("template_value", m.Meta["extra_field"][1])
|
||
}
|
||
fields = strings.Split(m.Option("extra_fields"), " ")
|
||
switch v := m.Confv("template_value").(type) {
|
||
case map[string]interface{}:
|
||
for _, k := range fields {
|
||
if k == "" {
|
||
continue
|
||
}
|
||
if val, ok := v[k]; ok {
|
||
m.Add("append", k, fmt.Sprintf(format, val))
|
||
} else {
|
||
m.Add("append", k, fmt.Sprintf(format, ""))
|
||
}
|
||
}
|
||
m.Meta[m.Option("extra_field")][i] = ""
|
||
case float64:
|
||
m.Meta[m.Option("extra_field")][i] = fmt.Sprintf(format, int(v))
|
||
case nil:
|
||
m.Meta[m.Option("extra_field")][i] = ""
|
||
default:
|
||
m.Meta[m.Option("extra_field")][i] = fmt.Sprintf(format, v)
|
||
}
|
||
}
|
||
}
|
||
|
||
if m.Has("trans_field") {
|
||
trans := map[string]string{}
|
||
for i := 0; i < len(m.Meta["trans_map"]); i += 2 {
|
||
trans[m.Meta["trans_map"][i]] = m.Meta["trans_map"][i+1]
|
||
}
|
||
for i := 0; i < len(m.Meta[m.Option("trans_field")]); i++ {
|
||
if t, ok := trans[m.Meta[m.Option("trans_field")][i]]; ok {
|
||
m.Meta[m.Option("trans_field")][i] = t
|
||
}
|
||
}
|
||
}
|
||
|
||
m.Color(31, table).Echo(" %s %s %s %s %s %v\n", where, group, order, limit, offset, m.Meta["other"])
|
||
|
||
m.Table(func(maps map[string]string, lists []string, line int) bool {
|
||
for i, v := range lists {
|
||
if line == -1 {
|
||
m.Color(32, v)
|
||
} else {
|
||
m.Echo(v)
|
||
}
|
||
if i < len(lists)-1 {
|
||
m.Echo(m.Conf("csv_col_sep"))
|
||
}
|
||
}
|
||
m.Echo(m.Conf("csv_row_sep"))
|
||
return true
|
||
})
|
||
}
|
||
}},
|
||
"set": &ctx.Command{Name: "set table [field value] where condition", Help: "查看或选择数据库信息", Form: map[string]int{"where": 1}, Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
|
||
if mdb, ok := m.Target().Server.(*MDB); m.Assert(ok) && mdb.DB != nil {
|
||
sql := []string{"update", arg[0], "set"}
|
||
fields := []string{}
|
||
for i := 1; i < len(arg)-1; i += 2 {
|
||
sql = append(sql, fmt.Sprintf("%s='%s'", arg[i], arg[i+1]))
|
||
fields = append(fields, arg[i])
|
||
if i < len(arg)-2 {
|
||
sql = append(sql, ",")
|
||
}
|
||
}
|
||
sql = append(sql, fmt.Sprintf("where %s", m.Option("where")))
|
||
m.Spawn().Cmd("exec", strings.Join(sql, " "))
|
||
msg := m.Spawn().Cmd("show", arg[0], fields, "where", m.Option("where"))
|
||
m.Copy(msg, "result").Copy(msg, "append")
|
||
}
|
||
}},
|
||
},
|
||
}
|
||
|
||
func init() {
|
||
mdb := &MDB{}
|
||
mdb.Context = Index
|
||
ctx.Index.Register(Index, mdb)
|
||
}
|