move generic db functions to dbutil (for use with PromptCentral)

This commit is contained in:
sawka
2023-03-27 14:11:02 -07:00
parent 29205ba586
commit b0269d5228
6 changed files with 405 additions and 357 deletions
+175
View File
@@ -0,0 +1,175 @@
package dbutil
import (
"database/sql/driver"
"encoding/json"
"fmt"
"strconv"
)
func QuickSetStr(strVal *string, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
ival, ok := v.(int64)
if ok {
*strVal = strconv.FormatInt(ival, 10)
return
}
str, ok := v.(string)
if !ok {
return
}
*strVal = str
}
func QuickSetInt64(ival *int64, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
sqlInt, ok := v.(int64)
if !ok {
return
}
*ival = sqlInt
}
func QuickSetBool(bval *bool, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
sqlInt, ok := v.(int64)
if ok {
if sqlInt > 0 {
*bval = true
}
return
}
sqlBool, ok := v.(bool)
if ok {
*bval = sqlBool
}
}
func QuickSetBytes(bval *[]byte, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
sqlBytes, ok := v.([]byte)
if ok {
*bval = sqlBytes
}
}
func getByteArr(m map[string]any, name string, def string) ([]byte, bool) {
v, ok := m[name]
if !ok {
return nil, false
}
barr, ok := v.([]byte)
if !ok {
str, ok := v.(string)
if !ok {
return nil, false
}
barr = []byte(str)
}
if len(barr) == 0 {
barr = []byte(def)
}
return barr, true
}
func QuickSetJson(ptr interface{}, m map[string]interface{}, name string) {
barr, ok := getByteArr(m, name, "{}")
if !ok {
return
}
json.Unmarshal(barr, ptr)
}
func QuickSetNullableJson(ptr interface{}, m map[string]interface{}, name string) {
barr, ok := getByteArr(m, name, "null")
if !ok {
return
}
json.Unmarshal(barr, ptr)
}
func QuickSetJsonArr(ptr interface{}, m map[string]interface{}, name string) {
barr, ok := getByteArr(m, name, "[]")
if !ok {
return
}
json.Unmarshal(barr, ptr)
}
func QuickNullableJson(v interface{}) string {
if v == nil {
return "null"
}
barr, _ := json.Marshal(v)
return string(barr)
}
func QuickJson(v interface{}) string {
if v == nil {
return "{}"
}
barr, _ := json.Marshal(v)
return string(barr)
}
func QuickJsonBytes(v interface{}) []byte {
if v == nil {
return []byte("{}")
}
barr, _ := json.Marshal(v)
return barr
}
func QuickJsonArr(v interface{}) string {
if v == nil {
return "[]"
}
barr, _ := json.Marshal(v)
return string(barr)
}
func QuickJsonArrBytes(v interface{}) []byte {
if v == nil {
return []byte("[]")
}
barr, _ := json.Marshal(v)
return barr
}
func QuickScanJson(ptr interface{}, val interface{}) error {
barrVal, ok := val.([]byte)
if !ok {
strVal, ok := val.(string)
if !ok {
return fmt.Errorf("cannot scan '%T' into '%T'", val, ptr)
}
barrVal = []byte(strVal)
}
if len(barrVal) == 0 {
barrVal = []byte("{}")
}
return json.Unmarshal(barrVal, ptr)
}
func QuickValueJson(v interface{}) (driver.Value, error) {
if v == nil {
return "{}", nil
}
barr, err := json.Marshal(v)
if err != nil {
return nil, err
}
return string(barr), nil
}
+183
View File
@@ -0,0 +1,183 @@
package dbutil
import (
"fmt"
"reflect"
"strings"
"github.com/sawka/txwrap"
)
type DBMappable interface {
UseDBMap()
}
type MapConverter interface {
ToMap() map[string]interface{}
FromMap(map[string]interface{}) bool
}
type HasSimpleKey interface {
GetSimpleKey() string
}
type MapConverterPtr[T any] interface {
MapConverter
*T
}
type DBMappablePtr[T any] interface {
DBMappable
*T
}
func FromMap[PT MapConverterPtr[T], T any](m map[string]any) PT {
if len(m) == 0 {
return nil
}
rtn := PT(new(T))
ok := rtn.FromMap(m)
if !ok {
return nil
}
return rtn
}
func GetMapGen[PT MapConverterPtr[T], T any](tx *txwrap.TxWrap, query string, args ...interface{}) PT {
m := tx.GetMap(query, args...)
return FromMap[PT](m)
}
func GetMappable[PT DBMappablePtr[T], T any](tx *txwrap.TxWrap, query string, args ...interface{}) PT {
rtn := PT(new(T))
m := tx.GetMap(query, args...)
if len(m) == 0 {
return nil
}
FromDBMap(rtn, m)
return rtn
}
func SelectMapsGen[PT MapConverterPtr[T], T any](tx *txwrap.TxWrap, query string, args ...interface{}) []PT {
var rtn []PT
marr := tx.SelectMaps(query, args...)
for _, m := range marr {
val := FromMap[PT](m)
if val != nil {
rtn = append(rtn, val)
}
}
return rtn
}
func MakeGenMap[T HasSimpleKey](arr []T) map[string]T {
rtn := make(map[string]T)
for _, val := range arr {
rtn[val.GetSimpleKey()] = val
}
return rtn
}
func isStructType(rt reflect.Type) bool {
if rt.Kind() == reflect.Struct {
return true
}
if rt.Kind() == reflect.Pointer && rt.Elem().Kind() == reflect.Struct {
return true
}
return false
}
func isByteArrayType(t reflect.Type) bool {
return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8
}
func ToDBMap(v DBMappable, useBytes bool) map[string]interface{} {
if v == nil {
return nil
}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
panic(fmt.Sprintf("invalid type %T (non-struct) passed to StructToDBMap", v))
}
rt := rv.Type()
m := make(map[string]interface{})
numFields := rt.NumField()
for i := 0; i < numFields; i++ {
field := rt.Field(i)
fieldVal := rv.FieldByIndex(field.Index)
dbName := field.Tag.Get("dbmap")
if dbName == "" {
dbName = strings.ToLower(field.Name)
}
if dbName == "-" {
continue
}
if isByteArrayType(field.Type) {
m[dbName] = fieldVal.Interface()
} else if field.Type.Kind() == reflect.Slice {
if useBytes {
m[dbName] = QuickJsonArrBytes(fieldVal.Interface())
} else {
m[dbName] = QuickJsonArr(fieldVal.Interface())
}
} else if isStructType(field.Type) {
if useBytes {
m[dbName] = QuickJsonBytes(fieldVal.Interface())
} else {
m[dbName] = QuickJson(fieldVal.Interface())
}
} else {
m[dbName] = fieldVal.Interface()
}
}
return m
}
func FromDBMap(v DBMappable, m map[string]interface{}) {
if v == nil {
panic("StructFromDBMap, v cannot be nil")
}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
panic(fmt.Sprintf("invalid type %T (non-struct) passed to StructFromDBMap", v))
}
rt := rv.Type()
numFields := rt.NumField()
for i := 0; i < numFields; i++ {
field := rt.Field(i)
fieldVal := rv.FieldByIndex(field.Index)
dbName := field.Tag.Get("dbmap")
if dbName == "" {
dbName = strings.ToLower(field.Name)
}
if dbName == "-" {
continue
}
if isByteArrayType(field.Type) {
barrVal := fieldVal.Addr().Interface()
QuickSetBytes(barrVal.(*[]byte), m, dbName)
} else if field.Type.Kind() == reflect.Slice {
QuickSetJsonArr(fieldVal.Addr().Interface(), m, dbName)
} else if isStructType(field.Type) {
QuickSetJson(fieldVal.Addr().Interface(), m, dbName)
} else if field.Type.Kind() == reflect.String {
strVal := fieldVal.Addr().Interface()
QuickSetStr(strVal.(*string), m, dbName)
} else if field.Type.Kind() == reflect.Int64 {
intVal := fieldVal.Addr().Interface()
QuickSetInt64(intVal.(*int64), m, dbName)
} else if field.Type.Kind() == reflect.Bool {
boolVal := fieldVal.Addr().Interface()
QuickSetBool(boolVal.(*bool), m, dbName)
} else {
panic(fmt.Sprintf("StructFromDBMap invalid field type %v in %T", fieldVal.Type(), v))
}
}
}
+30 -29
View File
@@ -15,6 +15,7 @@ import (
"github.com/scripthaus-dev/mshell/pkg/base"
"github.com/scripthaus-dev/mshell/pkg/packet"
"github.com/scripthaus-dev/mshell/pkg/shexec"
"github.com/scripthaus-dev/sh2-server/pkg/dbutil"
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
)
@@ -88,7 +89,7 @@ func GetAllRemotes(ctx context.Context) ([]*RemoteType, error) {
query := `SELECT * FROM remote ORDER BY remoteidx`
marr := tx.SelectMaps(query)
for _, m := range marr {
rtn = append(rtn, FromMap[*RemoteType](m))
rtn = append(rtn, dbutil.FromMap[*RemoteType](m))
}
return nil
})
@@ -103,7 +104,7 @@ func GetRemoteByAlias(ctx context.Context, alias string) (*RemoteType, error) {
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote WHERE remotealias = ?`
m := tx.GetMap(query, alias)
remote = FromMap[*RemoteType](m)
remote = dbutil.FromMap[*RemoteType](m)
return nil
})
if err != nil {
@@ -117,7 +118,7 @@ func GetRemoteById(ctx context.Context, remoteId string) (*RemoteType, error) {
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote WHERE remoteid = ?`
m := tx.GetMap(query, remoteId)
remote = FromMap[*RemoteType](m)
remote = dbutil.FromMap[*RemoteType](m)
return nil
})
if err != nil {
@@ -131,7 +132,7 @@ func GetLocalRemote(ctx context.Context) (*RemoteType, error) {
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote WHERE local`
m := tx.GetMap(query)
remote = FromMap[*RemoteType](m)
remote = dbutil.FromMap[*RemoteType](m)
return nil
})
if err != nil {
@@ -144,7 +145,7 @@ func GetRemoteByCanonicalName(ctx context.Context, cname string) (*RemoteType, e
var remote *RemoteType
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote WHERE remotecanonicalname = ?`
remote = GetMapGen[*RemoteType](tx, query, cname)
remote = dbutil.GetMapGen[*RemoteType](tx, query, cname)
return nil
})
if err != nil {
@@ -157,7 +158,7 @@ func GetRemoteByPhysicalId(ctx context.Context, physicalId string) (*RemoteType,
var remote *RemoteType
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote WHERE physicalid = ?`
remote = GetMapGen[*RemoteType](tx, query, physicalId)
remote = dbutil.GetMapGen[*RemoteType](tx, query, physicalId)
return nil
})
if err != nil {
@@ -342,7 +343,7 @@ func runHistoryQuery(tx *TxWrap, opts HistoryQueryOpts, realOffset int, itemLimi
marr := tx.SelectMaps(query, queryArgs...)
rtn := make([]*HistoryItemType, len(marr))
for idx, m := range marr {
hitem := FromMap[*HistoryItemType](m)
hitem := dbutil.FromMap[*HistoryItemType](m)
rtn[idx] = hitem
}
return rtn, nil
@@ -367,7 +368,7 @@ func GetHistoryItems(ctx context.Context, opts HistoryQueryOpts) (*HistoryQueryR
func GetHistoryItemByLineNum(ctx context.Context, screenId string, lineNum int) (*HistoryItemType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*HistoryItemType, error) {
query := `SELECT * FROM history WHERE screenid = ? AND linenum = ?`
hitem := GetMapGen[*HistoryItemType](tx, query, screenId, lineNum)
hitem := dbutil.GetMapGen[*HistoryItemType](tx, query, screenId, lineNum)
return hitem, nil
})
}
@@ -438,12 +439,12 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) {
session.Full = true
}
query = `SELECT * FROM screen ORDER BY archived, screenidx, archivedts`
update.Screens = SelectMapsGen[*ScreenType](tx, query)
update.Screens = dbutil.SelectMapsGen[*ScreenType](tx, query)
for _, screen := range update.Screens {
screen.Full = true
}
query = `SELECT * FROM remote_instance`
riArr := SelectMapsGen[*RemoteInstance](tx, query)
riArr := dbutil.SelectMapsGen[*RemoteInstance](tx, query)
for _, ri := range riArr {
s := sessionMap[ri.SessionId]
if s != nil {
@@ -459,14 +460,14 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) {
func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*ScreenLinesType, error) {
query := `SELECT screenid FROM screen WHERE screenid = ?`
screen := GetMappable[*ScreenLinesType](tx, query, screenId)
screen := dbutil.GetMappable[*ScreenLinesType](tx, query, screenId)
if screen == nil {
return nil, nil
}
query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum`
tx.Select(&screen.Lines, query, screen.ScreenId)
query = `SELECT * FROM cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE screenid = ?)`
screen.Cmds = SelectMapsGen[*CmdType](tx, query, screen.ScreenId)
screen.Cmds = dbutil.SelectMapsGen[*CmdType](tx, query, screen.ScreenId)
return screen, nil
})
}
@@ -475,7 +476,7 @@ func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType,
func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) ([]*ScreenType, error) {
query := `SELECT * FROM screen WHERE sessionid = ? ORDER BY archived, screenidx, archivedts`
rtn := SelectMapsGen[*ScreenType](tx, query, sessionId)
rtn := dbutil.SelectMapsGen[*ScreenType](tx, query, sessionId)
for _, screen := range rtn {
screen.Full = true
}
@@ -716,7 +717,7 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
func GetScreenById(ctx context.Context, screenId string) (*ScreenType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*ScreenType, error) {
query := `SELECT * FROM screen WHERE screenid = ?`
screen := GetMapGen[*ScreenType](tx, query, screenId)
screen := dbutil.GetMapGen[*ScreenType](tx, query, screenId)
screen.Full = true
return screen, nil
})
@@ -766,7 +767,7 @@ func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*L
var cmdRtn *CmdType
if lineVal.CmdId != "" {
query = `SELECT * FROM cmd WHERE screenid = ? AND cmdid = ?`
cmdRtn = GetMapGen[*CmdType](tx, query, screenId, lineVal.CmdId)
cmdRtn = dbutil.GetMapGen[*CmdType](tx, query, screenId, lineVal.CmdId)
}
return &lineVal, cmdRtn, nil
})
@@ -781,7 +782,7 @@ func GetLineCmdByCmdId(ctx context.Context, screenId string, cmdId string) (*Lin
return nil, nil, nil
}
query = `SELECT * FROM cmd WHERE screenid = ? AND cmdid = ?`
cmdRtn := GetMapGen[*CmdType](tx, query, screenId, cmdId)
cmdRtn := dbutil.GetMapGen[*CmdType](tx, query, screenId, cmdId)
return &lineVal, cmdRtn, nil
})
}
@@ -832,7 +833,7 @@ func GetCmdByScreenId(ctx context.Context, screenId string, cmdId string) (*CmdT
var cmd *CmdType
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM cmd WHERE screenid = ? AND cmdid = ?`
cmd = GetMapGen[*CmdType](tx, query, screenId, cmdId)
cmd = dbutil.GetMapGen[*CmdType](tx, query, screenId, cmdId)
return nil
})
if err != nil {
@@ -1181,7 +1182,7 @@ func GetRemoteInstance(ctx context.Context, sessionId string, screenId string, r
var ri *RemoteInstance
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND screenid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?`
ri = GetMapGen[*RemoteInstance](tx, query, sessionId, screenId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name)
ri = dbutil.GetMapGen[*RemoteInstance](tx, query, sessionId, screenId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name)
return nil
})
if txErr != nil {
@@ -1227,7 +1228,7 @@ func UpdateRemoteState(ctx context.Context, sessionId string, screenId string, r
return fmt.Errorf("cannot update remote instance state: %w", err)
}
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND screenid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?`
ri = GetMapGen[*RemoteInstance](tx, query, sessionId, screenId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name)
ri = dbutil.GetMapGen[*RemoteInstance](tx, query, sessionId, screenId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name)
if ri == nil {
ri = &RemoteInstance{
RIId: scbase.GenPromptUUID(),
@@ -1408,7 +1409,7 @@ func GetRunningScreenCmds(ctx context.Context, screenId string) ([]*CmdType, err
var rtn []*CmdType
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * from cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE screenid = ?) AND status = ?`
rtn = SelectMapsGen[*CmdType](tx, query, screenId, CmdStatusRunning)
rtn = dbutil.SelectMapsGen[*CmdType](tx, query, screenId, CmdStatusRunning)
return nil
})
if txErr != nil {
@@ -1824,7 +1825,7 @@ func GetFullState(ctx context.Context, ssPtr ShellStatePtr) (*packet.ShellState,
}
for idx, diffHash := range ssPtr.DiffHashArr {
query = `SELECT * FROM state_diff WHERE diffhash = ?`
stateDiff := GetMapGen[*StateDiff](tx, query, diffHash)
stateDiff := dbutil.GetMapGen[*StateDiff](tx, query, diffHash)
if stateDiff == nil {
return fmt.Errorf("ShellStateDiff %s not found", diffHash)
}
@@ -1953,7 +1954,7 @@ func GetRIsForScreen(ctx context.Context, sessionId string, screenId string) ([]
var rtn []*RemoteInstance
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND (screenid = '' OR screenid = ?)`
rtn = SelectMapsGen[*RemoteInstance](tx, query, sessionId, screenId)
rtn = dbutil.SelectMapsGen[*RemoteInstance](tx, query, sessionId, screenId)
return nil
})
if txErr != nil {
@@ -2112,12 +2113,12 @@ func GetBookmarks(ctx context.Context, tag string) ([]*BookmarkType, error) {
var query string
if tag == "" {
query = `SELECT * FROM bookmark`
bms = SelectMapsGen[*BookmarkType](tx, query)
bms = dbutil.SelectMapsGen[*BookmarkType](tx, query)
} else {
query = `SELECT * FROM bookmark WHERE EXISTS (SELECT 1 FROM json_each(tags) WHERE value = ?)`
bms = SelectMapsGen[*BookmarkType](tx, query, tag)
bms = dbutil.SelectMapsGen[*BookmarkType](tx, query, tag)
}
bmMap := MakeGenMap(bms)
bmMap := dbutil.MakeGenMap(bms)
var orders []bookmarkOrderType
query = `SELECT bookmarkid, orderidx FROM bookmark_order WHERE tag = ?`
tx.Select(&orders, query, tag)
@@ -2139,7 +2140,7 @@ func GetBookmarkById(ctx context.Context, bookmarkId string, tag string) (*Bookm
var rtn *BookmarkType
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM bookmark WHERE bookmarkid = ?`
rtn = GetMapGen[*BookmarkType](tx, query, bookmarkId)
rtn = dbutil.GetMapGen[*BookmarkType](tx, query, bookmarkId)
if rtn == nil {
return nil
}
@@ -2275,7 +2276,7 @@ func CreatePlaybook(ctx context.Context, name string) (*PlaybookType, error) {
func selectPlaybook(tx *TxWrap, playbookId string) *PlaybookType {
query := `SELECT * FROM playbook where playbookid = ?`
playbook := GetMapGen[*PlaybookType](tx, query, playbookId)
playbook := dbutil.GetMapGen[*PlaybookType](tx, query, playbookId)
return playbook
}
@@ -2363,7 +2364,7 @@ func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryIte
query := `SELECT * FROM line WHERE lineid IN (SELECT value FROM json_each(?))`
tx.Select(&lineArr, query, quickJsonArr(getLineIdsFromHistoryItems(historyItems)))
query = `SELECT * FROM cmd WHERE cmdid IN (SELECT value FROM json_each(?))`
cmdArr := SelectMapsGen[*CmdType](tx, query, quickJsonArr(getCmdIdsFromHistoryItems(historyItems)))
cmdArr := dbutil.SelectMapsGen[*CmdType](tx, query, quickJsonArr(getCmdIdsFromHistoryItems(historyItems)))
return lineArr, cmdArr, nil
})
}
@@ -2371,7 +2372,7 @@ func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryIte
func PurgeHistoryByIds(ctx context.Context, historyIds []string) ([]*HistoryItemType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) ([]*HistoryItemType, error) {
query := `SELECT * FROM history WHERE historyid IN (SELECT value FROM json_each(?))`
rtn := SelectMapsGen[*HistoryItemType](tx, query, quickJsonArr(historyIds))
rtn := dbutil.SelectMapsGen[*HistoryItemType](tx, query, quickJsonArr(historyIds))
query = `DELETE FROM history WHERE historyid IN (SELECT value FROM json_each(?))`
tx.Exec(query, quickJsonArr(historyIds))
for _, hitem := range rtn {
-169
View File
@@ -2,81 +2,8 @@ package sstore
import (
"context"
"fmt"
"reflect"
"strings"
)
type DBMappable interface {
UseDBMap()
}
type MapConverter interface {
ToMap() map[string]interface{}
FromMap(map[string]interface{}) bool
}
type HasSimpleKey interface {
GetSimpleKey() string
}
type MapConverterPtr[T any] interface {
MapConverter
*T
}
type DBMappablePtr[T any] interface {
DBMappable
*T
}
func FromMap[PT MapConverterPtr[T], T any](m map[string]any) PT {
if len(m) == 0 {
return nil
}
rtn := PT(new(T))
ok := rtn.FromMap(m)
if !ok {
return nil
}
return rtn
}
func GetMapGen[PT MapConverterPtr[T], T any](tx *TxWrap, query string, args ...interface{}) PT {
m := tx.GetMap(query, args...)
return FromMap[PT](m)
}
func GetMappable[PT DBMappablePtr[T], T any](tx *TxWrap, query string, args ...interface{}) PT {
rtn := PT(new(T))
m := tx.GetMap(query, args...)
if len(m) == 0 {
return nil
}
FromDBMap(rtn, m)
return rtn
}
func SelectMapsGen[PT MapConverterPtr[T], T any](tx *TxWrap, query string, args ...interface{}) []PT {
var rtn []PT
marr := tx.SelectMaps(query, args...)
for _, m := range marr {
val := FromMap[PT](m)
if val != nil {
rtn = append(rtn, val)
}
}
return rtn
}
func MakeGenMap[T HasSimpleKey](arr []T) map[string]T {
rtn := make(map[string]T)
for _, val := range arr {
rtn[val.GetSimpleKey()] = val
}
return rtn
}
func WithTxRtn[RT any](ctx context.Context, fn func(tx *TxWrap) (RT, error)) (RT, error) {
var rtn RT
txErr := WithTx(ctx, func(tx *TxWrap) error {
@@ -104,99 +31,3 @@ func WithTxRtn3[RT1 any, RT2 any](ctx context.Context, fn func(tx *TxWrap) (RT1,
})
return rtn1, rtn2, txErr
}
func isStructType(rt reflect.Type) bool {
if rt.Kind() == reflect.Struct {
return true
}
if rt.Kind() == reflect.Pointer && rt.Elem().Kind() == reflect.Struct {
return true
}
return false
}
func isByteArrayType(t reflect.Type) bool {
return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8
}
func ToDBMap(v DBMappable) map[string]interface{} {
if v == nil {
return nil
}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
panic(fmt.Sprintf("invalid type %T (non-struct) passed to StructToDBMap", v))
}
rt := rv.Type()
m := make(map[string]interface{})
numFields := rt.NumField()
for i := 0; i < numFields; i++ {
field := rt.Field(i)
fieldVal := rv.FieldByIndex(field.Index)
dbName := field.Tag.Get("dbmap")
if dbName == "" {
dbName = strings.ToLower(field.Name)
}
if dbName == "-" {
continue
}
if isByteArrayType(field.Type) {
m[dbName] = fieldVal.Interface()
} else if field.Type.Kind() == reflect.Slice {
m[dbName] = quickJsonArr(fieldVal.Interface())
} else if isStructType(field.Type) {
m[dbName] = quickJson(fieldVal.Interface())
} else {
m[dbName] = fieldVal.Interface()
}
}
return m
}
func FromDBMap(v DBMappable, m map[string]interface{}) {
if v == nil {
panic("StructFromDBMap, v cannot be nil")
}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
panic(fmt.Sprintf("invalid type %T (non-struct) passed to StructFromDBMap", v))
}
rt := rv.Type()
numFields := rt.NumField()
for i := 0; i < numFields; i++ {
field := rt.Field(i)
fieldVal := rv.FieldByIndex(field.Index)
dbName := field.Tag.Get("dbmap")
if dbName == "" {
dbName = strings.ToLower(field.Name)
}
if dbName == "-" {
continue
}
if isByteArrayType(field.Type) {
barrVal := fieldVal.Addr().Interface()
quickSetBytes(barrVal.(*[]byte), m, dbName)
} else if field.Type.Kind() == reflect.Slice {
quickSetJsonArr(fieldVal.Addr().Interface(), m, dbName)
} else if isStructType(field.Type) {
quickSetJson(fieldVal.Addr().Interface(), m, dbName)
} else if field.Type.Kind() == reflect.String {
strVal := fieldVal.Addr().Interface()
quickSetStr(strVal.(*string), m, dbName)
} else if field.Type.Kind() == reflect.Int64 {
intVal := fieldVal.Addr().Interface()
quickSetInt64(intVal.(*int64), m, dbName)
} else if field.Type.Kind() == reflect.Bool {
boolVal := fieldVal.Addr().Interface()
quickSetBool(boolVal.(*bool), m, dbName)
} else {
panic(fmt.Sprintf("StructFromDBMap invalid field type %v in %T", fieldVal.Type(), v))
}
}
}
+13 -156
View File
@@ -1,161 +1,18 @@
package sstore
import (
"database/sql/driver"
"encoding/json"
"fmt"
"strconv"
"github.com/scripthaus-dev/sh2-server/pkg/dbutil"
)
func quickSetStr(strVal *string, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
ival, ok := v.(int64)
if ok {
*strVal = strconv.FormatInt(ival, 10)
return
}
str, ok := v.(string)
if !ok {
return
}
*strVal = str
}
func quickSetInt64(ival *int64, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
sqlInt, ok := v.(int64)
if !ok {
return
}
*ival = sqlInt
}
func quickSetBool(bval *bool, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
sqlInt, ok := v.(int64)
if ok {
if sqlInt > 0 {
*bval = true
}
return
}
sqlBool, ok := v.(bool)
if ok {
*bval = sqlBool
}
}
func quickSetBytes(bval *[]byte, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
sqlBytes, ok := v.([]byte)
if ok {
*bval = sqlBytes
}
}
func quickSetJson(ptr interface{}, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
str, ok := v.(string)
if !ok {
return
}
if str == "" {
str = "{}"
}
json.Unmarshal([]byte(str), ptr)
}
func quickSetNullableJson(ptr interface{}, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
str, ok := v.(string)
if !ok {
return
}
if str == "" {
str = "null"
}
json.Unmarshal([]byte(str), ptr)
}
func quickSetJsonArr(ptr interface{}, m map[string]interface{}, name string) {
v, ok := m[name]
if !ok {
return
}
str, ok := v.(string)
if !ok {
return
}
if str == "" {
str = "[]"
}
json.Unmarshal([]byte(str), ptr)
}
func quickNullableJson(v interface{}) string {
if v == nil {
return "null"
}
barr, _ := json.Marshal(v)
return string(barr)
}
func quickJson(v interface{}) string {
if v == nil {
return "{}"
}
barr, _ := json.Marshal(v)
return string(barr)
}
func quickJsonArr(v interface{}) string {
if v == nil {
return "[]"
}
barr, _ := json.Marshal(v)
return string(barr)
}
func quickScanJson(ptr interface{}, val interface{}) error {
barrVal, ok := val.([]byte)
if !ok {
strVal, ok := val.(string)
if !ok {
return fmt.Errorf("cannot scan '%T' into '%T'", val, ptr)
}
barrVal = []byte(strVal)
}
if len(barrVal) == 0 {
barrVal = []byte("{}")
}
return json.Unmarshal(barrVal, ptr)
}
func quickValueJson(v interface{}) (driver.Value, error) {
if v == nil {
return "{}", nil
}
barr, err := json.Marshal(v)
if err != nil {
return nil, err
}
return string(barr), nil
}
var quickSetStr = dbutil.QuickSetStr
var quickSetInt64 = dbutil.QuickSetInt64
var quickSetBool = dbutil.QuickSetBool
var quickSetBytes = dbutil.QuickSetBytes
var quickSetJson = dbutil.QuickSetJson
var quickSetNullableJson = dbutil.QuickSetNullableJson
var quickSetJsonArr = dbutil.QuickSetJsonArr
var quickNullableJson = dbutil.QuickNullableJson
var quickJson = dbutil.QuickJson
var quickJsonArr = dbutil.QuickJsonArr
var quickScanJson = dbutil.QuickScanJson
var quickValueJson = dbutil.QuickValueJson
+4 -3
View File
@@ -22,6 +22,7 @@ import (
"github.com/sawka/txwrap"
"github.com/scripthaus-dev/mshell/pkg/base"
"github.com/scripthaus-dev/mshell/pkg/packet"
"github.com/scripthaus-dev/sh2-server/pkg/dbutil"
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
_ "github.com/mattn/go-sqlite3"
@@ -1095,7 +1096,7 @@ func createClientData(tx *TxWrap) error {
}
query := `INSERT INTO client ( clientid, userid, activesessionid, userpublickeybytes, userprivatekeybytes, winsize)
VALUES (:clientid,:userid,:activesessionid,:userpublickeybytes,:userprivatekeybytes,:winsize)`
tx.NamedExec(query, ToDBMap(c))
tx.NamedExec(query, dbutil.ToDBMap(c, false))
log.Printf("create new clientid[%s] userid[%s] with public/private keypair\n", c.ClientId, c.UserId)
return nil
}
@@ -1113,7 +1114,7 @@ func EnsureClientData(ctx context.Context) (*ClientData, error) {
return nil, createErr
}
}
cdata := GetMappable[*ClientData](tx, `SELECT * FROM client`)
cdata := dbutil.GetMappable[*ClientData](tx, `SELECT * FROM client`)
if cdata == nil {
return nil, fmt.Errorf("no client data found")
}
@@ -1148,7 +1149,7 @@ func EnsureClientData(ctx context.Context) (*ClientData, error) {
func GetCmdMigrationInfo(ctx context.Context) (*ClientMigrationData, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*ClientMigrationData, error) {
cdata := GetMappable[*ClientData](tx, `SELECT * FROM client`)
cdata := dbutil.GetMappable[*ClientData](tx, `SELECT * FROM client`)
if cdata == nil {
return nil, fmt.Errorf("no client data found")
}