2021-05-08 18:00:33 +08:00
|
|
|
|
package application
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2022-07-20 23:25:52 +08:00
|
|
|
|
"context"
|
2021-07-28 18:03:19 +08:00
|
|
|
|
"database/sql"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
2022-07-23 16:41:04 +08:00
|
|
|
|
"mayfly-go/internal/constant"
|
2022-09-09 18:26:08 +08:00
|
|
|
|
"mayfly-go/internal/db/domain/entity"
|
|
|
|
|
|
"mayfly-go/internal/db/domain/repository"
|
|
|
|
|
|
machineapp "mayfly-go/internal/machine/application"
|
|
|
|
|
|
"mayfly-go/internal/machine/infrastructure/machine"
|
2022-06-02 17:41:11 +08:00
|
|
|
|
"mayfly-go/pkg/biz"
|
|
|
|
|
|
"mayfly-go/pkg/cache"
|
|
|
|
|
|
"mayfly-go/pkg/global"
|
|
|
|
|
|
"mayfly-go/pkg/model"
|
|
|
|
|
|
"mayfly-go/pkg/utils"
|
2022-07-20 01:37:25 +00:00
|
|
|
|
"net"
|
2022-06-16 15:55:18 +08:00
|
|
|
|
"reflect"
|
2022-03-24 17:50:44 +08:00
|
|
|
|
"strconv"
|
2021-07-28 18:03:19 +08:00
|
|
|
|
"strings"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
2022-07-10 12:14:06 +08:00
|
|
|
|
|
2022-07-20 01:37:25 +00:00
|
|
|
|
"github.com/go-sql-driver/mysql"
|
2022-07-20 23:25:52 +08:00
|
|
|
|
"github.com/lib/pq"
|
2021-05-08 18:00:33 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
type Db interface {
|
2022-06-16 15:55:18 +08:00
|
|
|
|
// 分页获取
|
2021-07-28 18:03:19 +08:00
|
|
|
|
GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
2021-05-08 18:00:33 +08:00
|
|
|
|
|
2021-09-11 14:04:09 +08:00
|
|
|
|
Count(condition *entity.Db) int64
|
|
|
|
|
|
|
2021-05-08 18:00:33 +08:00
|
|
|
|
// 根据条件获取
|
|
|
|
|
|
GetDbBy(condition *entity.Db, cols ...string) error
|
|
|
|
|
|
|
|
|
|
|
|
// 根据id获取
|
|
|
|
|
|
GetById(id uint64, cols ...string) *entity.Db
|
|
|
|
|
|
|
|
|
|
|
|
Save(entity *entity.Db)
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 删除数据库信息
|
|
|
|
|
|
Delete(id uint64)
|
|
|
|
|
|
|
|
|
|
|
|
// 获取数据库连接实例
|
2022-05-08 14:10:57 +08:00
|
|
|
|
// @param id 数据库实例id
|
|
|
|
|
|
// @param db 数据库
|
|
|
|
|
|
GetDbInstance(id uint64, db string) *DbInstance
|
2022-07-23 16:41:04 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取数据库实例的所有数据库列表
|
|
|
|
|
|
GetDatabases(entity *entity.Db) []string
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-09 18:26:08 +08:00
|
|
|
|
func newDbApp(dbRepo repository.Db, dbSqlRepo repository.DbSql) Db {
|
|
|
|
|
|
return &dbAppImpl{
|
|
|
|
|
|
dbRepo: dbRepo,
|
|
|
|
|
|
dbSqlRepo: dbSqlRepo,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
type dbAppImpl struct {
|
|
|
|
|
|
dbRepo repository.Db
|
|
|
|
|
|
dbSqlRepo repository.DbSql
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 分页获取数据库信息列表
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
2021-05-08 18:00:33 +08:00
|
|
|
|
return d.dbRepo.GetDbList(condition, pageParam, toEntity, orderBy...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-11 14:04:09 +08:00
|
|
|
|
func (d *dbAppImpl) Count(condition *entity.Db) int64 {
|
|
|
|
|
|
return d.dbRepo.Count(condition)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-08 18:00:33 +08:00
|
|
|
|
// 根据条件获取
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) GetDbBy(condition *entity.Db, cols ...string) error {
|
2021-05-08 18:00:33 +08:00
|
|
|
|
return d.dbRepo.GetDb(condition, cols...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据id获取
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) GetById(id uint64, cols ...string) *entity.Db {
|
2021-05-08 18:00:33 +08:00
|
|
|
|
return d.dbRepo.GetById(id, cols...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) Save(dbEntity *entity.Db) {
|
|
|
|
|
|
// 默认tcp连接
|
2022-07-20 23:25:52 +08:00
|
|
|
|
dbEntity.Network = dbEntity.GetNetwork()
|
2022-07-20 01:37:25 +00:00
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 测试连接
|
2022-05-08 14:10:57 +08:00
|
|
|
|
if dbEntity.Password != "" {
|
2022-07-20 23:25:52 +08:00
|
|
|
|
TestConnection(dbEntity)
|
2022-05-08 14:10:57 +08:00
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
|
|
|
|
|
// 查找是否存在该库
|
2022-05-08 14:10:57 +08:00
|
|
|
|
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, EnvId: dbEntity.EnvId}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
err := d.GetDbBy(oldDb)
|
|
|
|
|
|
|
|
|
|
|
|
if dbEntity.Id == 0 {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
biz.NotEmpty(dbEntity.Password, "密码不能为空")
|
|
|
|
|
|
biz.IsTrue(err != nil, "该数据库实例已存在")
|
2022-08-02 21:44:01 +08:00
|
|
|
|
dbEntity.PwdEncrypt()
|
2021-07-28 18:03:19 +08:00
|
|
|
|
d.dbRepo.Insert(dbEntity)
|
2022-05-08 14:10:57 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果存在该库,则校验修改的库是否为该库
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
biz.IsTrue(oldDb.Id == dbEntity.Id, "该数据库实例已存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbId := dbEntity.Id
|
|
|
|
|
|
old := d.GetById(dbId)
|
|
|
|
|
|
|
|
|
|
|
|
var oldDbs []interface{}
|
|
|
|
|
|
for _, v := range strings.Split(old.Database, " ") {
|
2022-07-20 23:25:52 +08:00
|
|
|
|
// 关闭数据库连接
|
|
|
|
|
|
CloseDb(dbEntity.Id, v)
|
2022-05-08 14:10:57 +08:00
|
|
|
|
oldDbs = append(oldDbs, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var newDbs []interface{}
|
|
|
|
|
|
for _, v := range strings.Split(dbEntity.Database, " ") {
|
|
|
|
|
|
newDbs = append(newDbs, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 比较新旧数据库列表,需要将移除的数据库相关联的信息删除
|
|
|
|
|
|
_, delDb, _ := utils.ArrayCompare(newDbs, oldDbs, func(i1, i2 interface{}) bool {
|
|
|
|
|
|
return i1.(string) == i2.(string)
|
|
|
|
|
|
})
|
|
|
|
|
|
for _, v := range delDb {
|
|
|
|
|
|
// 删除该库关联的所有sql记录
|
|
|
|
|
|
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: dbId, Db: v.(string)})
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2022-05-08 14:10:57 +08:00
|
|
|
|
|
2022-08-02 21:44:01 +08:00
|
|
|
|
dbEntity.PwdEncrypt()
|
2022-05-08 14:10:57 +08:00
|
|
|
|
d.dbRepo.Update(dbEntity)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (d *dbAppImpl) Delete(id uint64) {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
db := d.GetById(id)
|
|
|
|
|
|
dbs := strings.Split(db.Database, " ")
|
|
|
|
|
|
for _, v := range dbs {
|
|
|
|
|
|
// 关闭连接
|
|
|
|
|
|
CloseDb(id, v)
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
d.dbRepo.Delete(id)
|
|
|
|
|
|
// 删除该库下用户保存的所有sql信息
|
|
|
|
|
|
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
func (d *dbAppImpl) GetDatabases(ed *entity.Db) []string {
|
2022-08-10 19:46:17 +08:00
|
|
|
|
ed.Network = ed.GetNetwork()
|
2022-07-23 16:41:04 +08:00
|
|
|
|
databases := make([]string, 0)
|
|
|
|
|
|
var dbConn *sql.DB
|
|
|
|
|
|
var metaDb string
|
|
|
|
|
|
var getDatabasesSql string
|
|
|
|
|
|
if ed.Type == entity.DbTypeMysql {
|
|
|
|
|
|
metaDb = "information_schema"
|
|
|
|
|
|
getDatabasesSql = "SELECT SCHEMA_NAME AS dbname FROM SCHEMATA"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
metaDb = "postgres"
|
|
|
|
|
|
getDatabasesSql = "SELECT datname AS dbname FROM pg_database"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbConn, err := GetDbConn(ed, metaDb)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, "数据库连接失败: %s")
|
|
|
|
|
|
defer dbConn.Close()
|
|
|
|
|
|
|
|
|
|
|
|
_, res, err := SelectDataByDb(dbConn, getDatabasesSql)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, "获取数据库列表失败")
|
|
|
|
|
|
for _, re := range res {
|
|
|
|
|
|
databases = append(databases, re["dbname"].(string))
|
|
|
|
|
|
}
|
|
|
|
|
|
return databases
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-18 17:57:33 +08:00
|
|
|
|
var mutex sync.Mutex
|
|
|
|
|
|
|
2022-05-08 14:10:57 +08:00
|
|
|
|
func (da *dbAppImpl) GetDbInstance(id uint64, db string) *DbInstance {
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// Id不为0,则为需要缓存
|
|
|
|
|
|
needCache := id != 0
|
|
|
|
|
|
if needCache {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
load, ok := dbCache.Get(GetDbCacheKey(id, db))
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if ok {
|
|
|
|
|
|
return load.(*DbInstance)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-07-26 18:32:45 +08:00
|
|
|
|
mutex.Lock()
|
2022-07-23 16:41:04 +08:00
|
|
|
|
defer mutex.Unlock()
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
d := da.GetById(id)
|
2022-08-02 21:44:01 +08:00
|
|
|
|
// 密码解密
|
|
|
|
|
|
d.PwdDecrypt()
|
2021-07-28 18:03:19 +08:00
|
|
|
|
biz.NotNil(d, "数据库信息不存在")
|
2022-05-08 14:10:57 +08:00
|
|
|
|
biz.IsTrue(strings.Contains(d.Database, db), "未配置该库的操作权限")
|
2022-07-20 23:25:52 +08:00
|
|
|
|
|
|
|
|
|
|
cacheKey := GetDbCacheKey(id, db)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
dbi := &DbInstance{Id: cacheKey, Type: d.Type, ProjectId: d.ProjectId, sshTunnelMachineId: d.SshTunnelMachineId}
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
DB, err := GetDbConn(d, db)
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
dbi.Close()
|
2022-05-27 15:45:12 +08:00
|
|
|
|
global.Log.Errorf("连接db失败: %s:%d/%s", d.Host, d.Port, db)
|
2022-07-20 23:25:52 +08:00
|
|
|
|
panic(biz.NewBizErr(fmt.Sprintf("数据库连接失败: %s", err.Error())))
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 最大连接周期,超过时间的连接就close
|
2022-05-12 10:34:16 +08:00
|
|
|
|
// DB.SetConnMaxLifetime(100 * time.Second)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 设置最大连接数
|
|
|
|
|
|
DB.SetMaxOpenConns(2)
|
|
|
|
|
|
// 设置闲置连接数
|
|
|
|
|
|
DB.SetMaxIdleConns(1)
|
|
|
|
|
|
|
2022-07-20 23:25:52 +08:00
|
|
|
|
dbi.db = DB
|
|
|
|
|
|
global.Log.Infof("连接db: %s:%d/%s", d.Host, d.Port, db)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if needCache {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
dbCache.Put(cacheKey, dbi)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
return dbi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
2021-05-08 18:00:33 +08:00
|
|
|
|
|
2022-08-21 21:00:28 +08:00
|
|
|
|
// 单次最大查询数据集
|
|
|
|
|
|
const Max_Rows = 2000
|
|
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
// 客户端连接缓存,指定时间内没有访问则会被关闭, key为数据库实例id:数据库
|
|
|
|
|
|
var dbCache = cache.NewTimedCache(constant.DbConnExpireTime, 5*time.Second).
|
2021-08-18 17:57:33 +08:00
|
|
|
|
WithUpdateAccessTime(true).
|
|
|
|
|
|
OnEvicted(func(key interface{}, value interface{}) {
|
2022-05-12 10:34:16 +08:00
|
|
|
|
global.Log.Info(fmt.Sprintf("删除db连接缓存 id = %s", key))
|
2021-08-18 17:57:33 +08:00
|
|
|
|
value.(*DbInstance).Close()
|
|
|
|
|
|
})
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
func init() {
|
|
|
|
|
|
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
|
|
|
|
|
|
// 遍历所有db连接实例,若存在redis实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
|
|
|
|
|
items := dbCache.Items()
|
|
|
|
|
|
for _, v := range items {
|
|
|
|
|
|
if v.Value.(*DbInstance).sshTunnelMachineId == machineId {
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-08 14:10:57 +08:00
|
|
|
|
func GetDbCacheKey(dbId uint64, db string) string {
|
|
|
|
|
|
return fmt.Sprintf("%d:%s", dbId, db)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func GetDbInstanceByCache(id string) *DbInstance {
|
|
|
|
|
|
if load, ok := dbCache.Get(id); ok {
|
2021-07-28 18:03:19 +08:00
|
|
|
|
return load.(*DbInstance)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-20 23:25:52 +08:00
|
|
|
|
func TestConnection(d *entity.Db) {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
// 验证第一个库是否可以连接即可
|
|
|
|
|
|
DB, err := GetDbConn(d, strings.Split(d.Database, " ")[0])
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, "数据库连接失败: %s")
|
|
|
|
|
|
defer DB.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取数据库连接
|
|
|
|
|
|
func GetDbConn(d *entity.Db, db string) (*sql.DB, error) {
|
|
|
|
|
|
// SSH Conect
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if d.EnableSshTunnel == 1 && d.SshTunnelMachineId != 0 {
|
2022-09-09 18:26:08 +08:00
|
|
|
|
sshTunnelMachine := machineapp.GetMachineApp().GetSshTunnelMachine(d.SshTunnelMachineId)
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if d.Type == entity.DbTypeMysql {
|
|
|
|
|
|
mysql.RegisterDialContext(d.Network, func(ctx context.Context, addr string) (net.Conn, error) {
|
2022-07-24 18:54:23 +08:00
|
|
|
|
return sshTunnelMachine.GetDialConn("tcp", addr)
|
2022-07-20 23:25:52 +08:00
|
|
|
|
})
|
|
|
|
|
|
} else if d.Type == entity.DbTypePostgres {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
_, err := pq.DialOpen(&PqSqlDialer{sshTunnelMachine: sshTunnelMachine}, getDsn(d, db))
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
panic(biz.NewBizErr(fmt.Sprintf("postgres隧道连接失败: %s", err.Error())))
|
|
|
|
|
|
}
|
2022-07-20 01:37:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
DB, err := sql.Open(d.Type, getDsn(d, db))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
err = DB.Ping()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
DB.Close()
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
return DB, nil
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
func SelectDataByDb(db *sql.DB, selectSql string) ([]string, []map[string]interface{}, error) {
|
|
|
|
|
|
rows, err := db.Query(selectSql)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
|
|
|
|
|
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if rows != nil {
|
|
|
|
|
|
rows.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2022-06-16 15:55:18 +08:00
|
|
|
|
colTypes, _ := rows.ColumnTypes()
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 这里表示一行填充数据
|
2022-06-16 15:55:18 +08:00
|
|
|
|
scans := make([]interface{}, len(colTypes))
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 这里表示一行所有列的值,用[]byte表示
|
2022-06-16 15:55:18 +08:00
|
|
|
|
vals := make([][]byte, len(colTypes))
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 这里scans引用vals,把数据填充到[]byte里
|
|
|
|
|
|
for k := range vals {
|
|
|
|
|
|
scans[k] = &vals[k]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-16 15:55:18 +08:00
|
|
|
|
result := make([]map[string]interface{}, 0)
|
|
|
|
|
|
// 列名用于前端表头名称按照数据库与查询字段顺序显示
|
2021-07-28 18:03:19 +08:00
|
|
|
|
colNames := make([]string, 0)
|
|
|
|
|
|
// 是否第一次遍历,列名数组只需第一次遍历时加入
|
|
|
|
|
|
isFirst := true
|
2022-08-21 21:00:28 +08:00
|
|
|
|
rowNum := 0
|
2021-07-28 18:03:19 +08:00
|
|
|
|
for rows.Next() {
|
2022-08-21 21:00:28 +08:00
|
|
|
|
rowNum++
|
|
|
|
|
|
biz.IsTrue(rowNum <= Max_Rows, "结果集 > 2000, 请完善条件或分页信息")
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
|
|
|
|
|
|
err := rows.Scan(scans...)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
// 每行数据
|
2022-06-16 15:55:18 +08:00
|
|
|
|
rowData := make(map[string]interface{})
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 把vals中的数据复制到row中
|
2022-06-16 15:55:18 +08:00
|
|
|
|
for i, v := range vals {
|
|
|
|
|
|
colType := colTypes[i]
|
|
|
|
|
|
colName := colType.Name()
|
|
|
|
|
|
// 字段类型名
|
|
|
|
|
|
colScanType := colType.ScanType().Name()
|
2022-08-21 21:00:28 +08:00
|
|
|
|
// 如果是第一行,则将列名加入到列信息中,由于map是无序的,所有需要返回列名的有序数组
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if isFirst {
|
2022-06-16 15:55:18 +08:00
|
|
|
|
colNames = append(colNames, colName)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2022-06-30 16:42:25 +08:00
|
|
|
|
if v == nil {
|
|
|
|
|
|
rowData[colName] = nil
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 这里把[]byte数据转成string
|
2022-06-16 15:55:18 +08:00
|
|
|
|
stringV := string(v)
|
|
|
|
|
|
if stringV == "" {
|
|
|
|
|
|
rowData[colName] = stringV
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.Contains(colScanType, "int") || strings.Contains(colScanType, "Int") {
|
|
|
|
|
|
intV, _ := strconv.Atoi(stringV)
|
|
|
|
|
|
switch colType.ScanType().Kind() {
|
|
|
|
|
|
case reflect.Int8:
|
|
|
|
|
|
rowData[colName] = int8(intV)
|
|
|
|
|
|
case reflect.Uint8:
|
|
|
|
|
|
rowData[colName] = uint8(intV)
|
|
|
|
|
|
case reflect.Int64:
|
|
|
|
|
|
rowData[colName] = int64(intV)
|
|
|
|
|
|
case reflect.Uint64:
|
|
|
|
|
|
rowData[colName] = uint64(intV)
|
|
|
|
|
|
case reflect.Uint:
|
|
|
|
|
|
rowData[colName] = uint(intV)
|
|
|
|
|
|
default:
|
|
|
|
|
|
rowData[colName] = intV
|
|
|
|
|
|
}
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.Contains(colScanType, "float") || strings.Contains(colScanType, "Float") {
|
|
|
|
|
|
floatV, _ := strconv.ParseFloat(stringV, 64)
|
|
|
|
|
|
rowData[colName] = floatV
|
|
|
|
|
|
} else {
|
|
|
|
|
|
rowData[colName] = stringV
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2022-06-16 15:55:18 +08:00
|
|
|
|
// 放入结果集
|
2021-07-28 18:03:19 +08:00
|
|
|
|
result = append(result, rowData)
|
|
|
|
|
|
isFirst = false
|
|
|
|
|
|
}
|
|
|
|
|
|
return colNames, result, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
type PqSqlDialer struct {
|
|
|
|
|
|
sshTunnelMachine *machine.SshTunnelMachine
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (pd *PqSqlDialer) Dial(network, address string) (net.Conn, error) {
|
|
|
|
|
|
if sshConn, err := pd.sshTunnelMachine.GetDialConn("tcp", address); err == nil {
|
|
|
|
|
|
// 将ssh conn包装,否则redis内部设置超时会报错,ssh conn不支持设置超时会返回错误: ssh: tcpChan: deadline not supported
|
|
|
|
|
|
return &utils.WrapSshConn{Conn: sshConn}, nil
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
func (pd *PqSqlDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
|
|
|
|
|
|
return pd.Dial(network, address)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// db实例
|
|
|
|
|
|
type DbInstance struct {
|
|
|
|
|
|
Id string
|
|
|
|
|
|
Type string
|
|
|
|
|
|
ProjectId uint64
|
|
|
|
|
|
db *sql.DB
|
|
|
|
|
|
sshTunnelMachineId uint64
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行查询语句
|
|
|
|
|
|
// 依次返回 列名数组,结果map,错误
|
|
|
|
|
|
func (d *DbInstance) SelectData(execSql string) ([]string, []map[string]interface{}, error) {
|
|
|
|
|
|
execSql = strings.Trim(execSql, " ")
|
|
|
|
|
|
isSelect := strings.HasPrefix(execSql, "SELECT") || strings.HasPrefix(execSql, "select")
|
|
|
|
|
|
isShow := strings.HasPrefix(execSql, "show")
|
|
|
|
|
|
isExplain := strings.HasPrefix(execSql, "explain")
|
|
|
|
|
|
|
|
|
|
|
|
if !isSelect && !isShow && !isExplain {
|
|
|
|
|
|
return nil, nil, errors.New("该sql非查询语句")
|
|
|
|
|
|
}
|
|
|
|
|
|
return SelectDataByDb(d.db, execSql)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 执行 update, insert, delete,建表等sql
|
|
|
|
|
|
// 返回影响条数和错误
|
|
|
|
|
|
func (d *DbInstance) Exec(sql string) (int64, error) {
|
|
|
|
|
|
res, err := d.db.Exec(sql)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return 0, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return res.RowsAffected()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 获取数据库元信息实现接口
|
|
|
|
|
|
func (di *DbInstance) GetMeta() DbMetadata {
|
|
|
|
|
|
dbType := di.Type
|
|
|
|
|
|
if dbType == entity.DbTypeMysql {
|
|
|
|
|
|
return &MysqlMetadata{di: di}
|
|
|
|
|
|
}
|
|
|
|
|
|
if dbType == entity.DbTypePostgres {
|
|
|
|
|
|
return &PgsqlMetadata{di: di}
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-18 17:57:33 +08:00
|
|
|
|
// 关闭连接
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *DbInstance) Close() {
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if d.db != nil {
|
|
|
|
|
|
if err := d.db.Close(); err != nil {
|
|
|
|
|
|
global.Log.Errorf("关闭数据库实例[%s]连接失败: %s", d.Id, err.Error())
|
|
|
|
|
|
}
|
2022-07-23 16:41:04 +08:00
|
|
|
|
d.db = nil
|
2022-07-20 23:25:52 +08:00
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取dataSourceName
|
2022-07-23 16:41:04 +08:00
|
|
|
|
func getDsn(d *entity.Db, db string) string {
|
2022-07-10 12:14:06 +08:00
|
|
|
|
var dsn string
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if d.Type == entity.DbTypeMysql {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
dsn = fmt.Sprintf("%s:%s@%s(%s:%d)/%s?timeout=8s", d.Username, d.Password, d.Network, d.Host, d.Port, db)
|
2022-07-10 12:14:06 +08:00
|
|
|
|
if d.Params != "" {
|
|
|
|
|
|
dsn = fmt.Sprintf("%s&%s", dsn, d.Params)
|
|
|
|
|
|
}
|
|
|
|
|
|
return dsn
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if d.Type == entity.DbTypePostgres {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
dsn = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", d.Host, d.Port, d.Username, d.Password, db)
|
2022-07-10 12:14:06 +08:00
|
|
|
|
if d.Params != "" {
|
|
|
|
|
|
dsn = fmt.Sprintf("%s %s", dsn, strings.Join(strings.Split(d.Params, "&"), " "))
|
|
|
|
|
|
}
|
|
|
|
|
|
return dsn
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-12 10:34:16 +08:00
|
|
|
|
// 删除db缓存并关闭该数据库所有连接
|
2022-05-08 14:10:57 +08:00
|
|
|
|
func CloseDb(dbId uint64, db string) {
|
2022-05-12 10:34:16 +08:00
|
|
|
|
dbCache.Delete(GetDbCacheKey(dbId, db))
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// -----------------------------------元数据-------------------------------------------
|
|
|
|
|
|
// 数据库元信息接口(表、列等元信息)
|
|
|
|
|
|
type DbMetadata interface {
|
|
|
|
|
|
// 获取表基础元信息, 如表名等
|
|
|
|
|
|
GetTables() []map[string]interface{}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取列元信息, 如列名等
|
|
|
|
|
|
GetColumns(tableNames ...string) []map[string]interface{}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表主键字段名,默认第一个字段
|
|
|
|
|
|
GetPrimaryKey(tablename string) string
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表信息,比GetTables获取更详细的表信息
|
|
|
|
|
|
GetTableInfos() []map[string]interface{}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表索引信息
|
|
|
|
|
|
GetTableIndex(tableName string) []map[string]interface{}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取建表ddl
|
|
|
|
|
|
GetCreateTableDdl(tableName string) []map[string]interface{}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 默认每次查询列元信息数量
|
|
|
|
|
|
const DEFAULT_COLUMN_SIZE = 2000
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// ---------------------------------- mysql元数据 -----------------------------------
|
2021-07-28 18:03:19 +08:00
|
|
|
|
const (
|
|
|
|
|
|
// mysql 表信息元数据
|
|
|
|
|
|
MYSQL_TABLE_MA = `SELECT table_name tableName, engine, table_comment tableComment,
|
|
|
|
|
|
create_time createTime from information_schema.tables
|
2022-06-25 19:52:11 +08:00
|
|
|
|
WHERE table_schema = (SELECT database()) LIMIT 2000`
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2021-08-18 17:57:33 +08:00
|
|
|
|
// mysql 表信息
|
|
|
|
|
|
MYSQL_TABLE_INFO = `SELECT table_name tableName, table_comment tableComment, table_rows tableRows,
|
|
|
|
|
|
data_length dataLength, index_length indexLength, create_time createTime
|
|
|
|
|
|
FROM information_schema.tables
|
2022-06-25 19:52:11 +08:00
|
|
|
|
WHERE table_schema = (SELECT database()) LIMIT 2000`
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
|
|
|
|
|
// mysql 索引信息
|
|
|
|
|
|
MYSQL_INDEX_INFO = `SELECT index_name indexName, column_name columnName, index_type indexType,
|
|
|
|
|
|
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
|
|
|
|
|
|
FROM information_schema.STATISTICS
|
2022-06-25 19:52:11 +08:00
|
|
|
|
WHERE table_schema = (SELECT database()) AND table_name = '%s' LIMIT 500`
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// mysql 列信息元数据
|
2022-07-10 12:14:06 +08:00
|
|
|
|
MYSQL_COLUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType,
|
2022-01-19 15:10:17 +08:00
|
|
|
|
column_comment columnComment, column_key columnKey, extra, is_nullable nullable from information_schema.columns
|
2022-06-25 19:52:11 +08:00
|
|
|
|
WHERE table_name in (%s) AND table_schema = (SELECT database()) ORDER BY tableName, ordinal_position LIMIT %d, %d`
|
2022-03-24 17:50:44 +08:00
|
|
|
|
|
|
|
|
|
|
// mysql 列信息元数据总数
|
|
|
|
|
|
MYSQL_COLOUMN_MA_COUNT = `SELECT COUNT(*) maNum from information_schema.columns
|
|
|
|
|
|
WHERE table_name in (%s) AND table_schema = (SELECT database())`
|
2021-07-28 18:03:19 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
type MysqlMetadata struct {
|
|
|
|
|
|
di *DbInstance
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表基础元信息, 如表名等
|
|
|
|
|
|
func (mm *MysqlMetadata) GetTables() []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := mm.di.SelectData(MYSQL_TABLE_MA)
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取列元信息, 如列名等
|
|
|
|
|
|
func (mm *MysqlMetadata) GetColumns(tableNames ...string) []map[string]interface{} {
|
|
|
|
|
|
var sql, tableName string
|
|
|
|
|
|
for i := 0; i < len(tableNames); i++ {
|
|
|
|
|
|
if i != 0 {
|
|
|
|
|
|
tableName = tableName + ", "
|
|
|
|
|
|
}
|
|
|
|
|
|
tableName = tableName + "'" + tableNames[i] + "'"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pageNum := 1
|
|
|
|
|
|
// 如果大于一个表,则统计列数并分页获取
|
|
|
|
|
|
if len(tableNames) > 1 {
|
|
|
|
|
|
countSql := fmt.Sprintf(MYSQL_COLOUMN_MA_COUNT, tableName)
|
|
|
|
|
|
_, countRes, _ := mm.di.SelectData(countSql)
|
2022-09-07 11:18:47 +08:00
|
|
|
|
maCount := 0
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 查询出所有列信息总数,手动分页获取所有数据
|
2022-09-07 11:18:47 +08:00
|
|
|
|
if count64, is64 := countRes[0]["maNum"].(int64); is64 {
|
|
|
|
|
|
maCount = int(count64)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
maCount = countRes[0]["maNum"].(int)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 计算需要查询的页数
|
|
|
|
|
|
pageNum = maCount / DEFAULT_COLUMN_SIZE
|
|
|
|
|
|
if maCount%DEFAULT_COLUMN_SIZE > 0 {
|
|
|
|
|
|
pageNum++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res := make([]map[string]interface{}, 0)
|
|
|
|
|
|
for index := 0; index < pageNum; index++ {
|
|
|
|
|
|
sql = fmt.Sprintf(MYSQL_COLUMN_MA, tableName, index*DEFAULT_COLUMN_SIZE, DEFAULT_COLUMN_SIZE)
|
|
|
|
|
|
_, result, err := mm.di.SelectData(sql)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
|
|
|
|
|
res = append(res, result...)
|
|
|
|
|
|
}
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表主键字段名,默认第一个字段
|
|
|
|
|
|
func (mm *MysqlMetadata) GetPrimaryKey(tablename string) string {
|
|
|
|
|
|
return mm.GetColumns(tablename)[0]["columnName"].(string)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表信息,比GetTableMetedatas获取更详细的表信息
|
|
|
|
|
|
func (mm *MysqlMetadata) GetTableInfos() []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := mm.di.SelectData(MYSQL_TABLE_INFO)
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表索引信息
|
|
|
|
|
|
func (mm *MysqlMetadata) GetTableIndex(tableName string) []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := mm.di.SelectData(MYSQL_INDEX_INFO)
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取建表ddl
|
|
|
|
|
|
func (mm *MysqlMetadata) GetCreateTableDdl(tableName string) []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := mm.di.SelectData(fmt.Sprintf("show create table %s ", tableName))
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------- pgsql元数据 -----------------------------------
|
2022-07-10 12:14:06 +08:00
|
|
|
|
const (
|
|
|
|
|
|
// postgres 表信息元数据
|
|
|
|
|
|
PGSQL_TABLE_MA = `SELECT obj_description(c.oid) AS "tableComment", c.relname AS "tableName" FROM pg_class c
|
|
|
|
|
|
JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = (select current_schema()) AND c.reltype > 0`
|
|
|
|
|
|
|
|
|
|
|
|
PGSQL_TABLE_INFO = `SELECT obj_description(c.oid) AS "tableComment", c.relname AS "tableName" FROM pg_class c
|
|
|
|
|
|
JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = (select current_schema()) AND c.reltype > 0`
|
|
|
|
|
|
|
|
|
|
|
|
PGSQL_INDEX_INFO = `SELECT indexname AS "indexName", indexdef AS "indexComment"
|
|
|
|
|
|
FROM pg_indexes WHERE schemaname = (select current_schema()) AND tablename = '%s'`
|
|
|
|
|
|
|
|
|
|
|
|
PGSQL_COLUMN_MA = `SELECT
|
|
|
|
|
|
C.relname AS "tableName",
|
|
|
|
|
|
A.attname AS "columnName",
|
|
|
|
|
|
concat_ws ( '', t.typname, SUBSTRING ( format_type ( a.atttypid, a.atttypmod ) FROM '\(.*\)' ) ) AS "columnType",
|
|
|
|
|
|
d.description AS "columnComment"
|
|
|
|
|
|
FROM
|
|
|
|
|
|
pg_attribute a LEFT JOIN pg_description d ON d.objoid = a.attrelid
|
|
|
|
|
|
AND d.objsubid = A.attnum
|
|
|
|
|
|
LEFT JOIN pg_class c ON A.attrelid = c.oid
|
|
|
|
|
|
LEFT JOIN pg_namespace pn ON c.relnamespace = pn.oid
|
|
|
|
|
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
A.attnum >= 0
|
|
|
|
|
|
AND pn.nspname = (select current_schema())
|
|
|
|
|
|
AND C.relname in (%s)
|
|
|
|
|
|
ORDER BY
|
|
|
|
|
|
C.relname DESC,
|
|
|
|
|
|
A.attnum ASC
|
|
|
|
|
|
OFFSET %d LIMIT %d
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
PGSQL_COLUMN_MA_COUNT = `SELECT COUNT(*) "maNum"
|
|
|
|
|
|
FROM
|
|
|
|
|
|
pg_attribute a LEFT JOIN pg_description d ON d.objoid = a.attrelid
|
|
|
|
|
|
AND d.objsubid = A.attnum
|
|
|
|
|
|
LEFT JOIN pg_class c ON A.attrelid = c.oid
|
|
|
|
|
|
LEFT JOIN pg_namespace pn ON c.relnamespace = pn.oid
|
|
|
|
|
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
A.attnum >= 0
|
|
|
|
|
|
AND pn.nspname = (select current_schema())
|
|
|
|
|
|
AND C.relname in (%s)
|
|
|
|
|
|
`
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
type PgsqlMetadata struct {
|
|
|
|
|
|
di *DbInstance
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表基础元信息, 如表名等
|
|
|
|
|
|
func (pm *PgsqlMetadata) GetTables() []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := pm.di.SelectData(PGSQL_TABLE_MA)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 获取列元信息, 如列名等
|
|
|
|
|
|
func (pm *PgsqlMetadata) GetColumns(tableNames ...string) []map[string]interface{} {
|
2021-07-28 18:03:19 +08:00
|
|
|
|
var sql, tableName string
|
|
|
|
|
|
for i := 0; i < len(tableNames); i++ {
|
|
|
|
|
|
if i != 0 {
|
|
|
|
|
|
tableName = tableName + ", "
|
|
|
|
|
|
}
|
|
|
|
|
|
tableName = tableName + "'" + tableNames[i] + "'"
|
|
|
|
|
|
}
|
2022-03-24 17:50:44 +08:00
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
pageNum := 1
|
|
|
|
|
|
// 如果大于一个表,则统计列数并分页获取
|
|
|
|
|
|
if len(tableNames) > 1 {
|
|
|
|
|
|
countSql := fmt.Sprintf(PGSQL_COLUMN_MA_COUNT, tableName)
|
|
|
|
|
|
_, countRes, _ := pm.di.SelectData(countSql)
|
2022-09-07 11:18:47 +08:00
|
|
|
|
maCount := 0
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 查询出所有列信息总数,手动分页获取所有数据
|
2022-09-07 11:18:47 +08:00
|
|
|
|
if count64, is64 := countRes[0]["maNum"].(int64); is64 {
|
|
|
|
|
|
maCount = int(count64)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
maCount = countRes[0]["maNum"].(int)
|
|
|
|
|
|
}
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 计算需要查询的页数
|
|
|
|
|
|
pageNum = maCount / DEFAULT_COLUMN_SIZE
|
|
|
|
|
|
if maCount%DEFAULT_COLUMN_SIZE > 0 {
|
|
|
|
|
|
pageNum++
|
|
|
|
|
|
}
|
2022-03-24 17:50:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-16 15:55:18 +08:00
|
|
|
|
res := make([]map[string]interface{}, 0)
|
2022-03-24 17:50:44 +08:00
|
|
|
|
for index := 0; index < pageNum; index++ {
|
2022-08-10 19:46:17 +08:00
|
|
|
|
sql = fmt.Sprintf(PGSQL_COLUMN_MA, tableName, index*DEFAULT_COLUMN_SIZE, DEFAULT_COLUMN_SIZE)
|
|
|
|
|
|
_, result, err := pm.di.SelectData(sql)
|
2022-03-24 17:50:44 +08:00
|
|
|
|
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
|
|
|
|
|
res = append(res, result...)
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
return res
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 获取表主键字段名,默认第一个字段
|
|
|
|
|
|
func (pm *PgsqlMetadata) GetPrimaryKey(tablename string) string {
|
|
|
|
|
|
return pm.GetColumns(tablename)[0]["columnName"].(string)
|
2022-06-16 15:55:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 获取表信息,比GetTables获取更详细的表信息
|
|
|
|
|
|
func (pm *PgsqlMetadata) GetTableInfos() []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := pm.di.SelectData(PGSQL_TABLE_INFO)
|
2021-08-18 17:57:33 +08:00
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 获取表索引信息
|
|
|
|
|
|
func (pm *PgsqlMetadata) GetTableIndex(tableName string) []map[string]interface{} {
|
|
|
|
|
|
_, res, _ := pm.di.SelectData(PGSQL_INDEX_INFO)
|
2021-08-18 17:57:33 +08:00
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
// 获取建表ddl
|
|
|
|
|
|
func (mm *PgsqlMetadata) GetCreateTableDdl(tableName string) []map[string]interface{} {
|
|
|
|
|
|
return nil
|
2021-08-18 17:57:33 +08:00
|
|
|
|
}
|