代码拉取完成,页面将自动刷新
/*
* Copyright 2018 Xiaomi, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package database
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/XiaoMi/soar/common"
)
// SHOW TABLE STATUS Syntax
// https://dev.mysql.com/doc/refman/5.7/en/show-table-status.html
// TableStatInfo 用以保存 show table status 之后获取的table信息
type TableStatInfo struct {
Name string
Rows []tableStatusRow
}
// tableStatusRow 用于 show table status value
type tableStatusRow struct {
Name string // 表名
Engine string // 该表使用的存储引擎
Version int // 该表的 .frm 文件版本号
RowFormat string // 该表使用的行存储格式
Rows int64 // 表行数,InnoDB 引擎中为预估值,甚至可能会有40%~50%的数值偏差
AvgRowLength int // 平均行长度
// MyISAM: Data_length 为数据文件的大小,单位为 bytes
// InnoDB: Data_length 为聚簇索引分配的近似内存量,单位为 bytes, 计算方式为聚簇索引数量乘以 InnoDB 页面大小
// 其他不同的存储引擎中该值的意义可能不尽相同
DataLength int
// MyISAM: Max_data_length 为数据文件长度的最大值。这是在给定使用的数据指针大小的情况下,可以存储在表中的数据的最大字节数
// InnoDB: 未使用
// 其他不同的存储引擎中该值的意义可能不尽相同
MaxDataLength int
// MyISAM: Index_length 为 index 文件的大小,单位为 bytes
// InnoDB: Index_length 为非聚簇索引分配的近似内存量,单位为 bytes,计算方式为非聚簇索引数量乘以 InnoDB 页面大小
// 其他不同的存储引擎中该值的意义可能不尽相同
IndexLength int
DataFree int // 已分配但未使用的字节数
AutoIncrement int // 下一个自增值
CreateTime time.Time // 创建时间
UpdateTime time.Time // 最近一次更新时间,该值不准确
CheckTime time.Time // 上次检查时间
Collation string // 字符集及排序规则信息
Checksum string // 校验和
CreateOptions string // 创建表的时候的时候一切其他属性
Comment string // 注释
}
// newTableStat 构造 table Stat 对象
func newTableStat(tableName string) *TableStatInfo {
return &TableStatInfo{
Name: tableName,
Rows: make([]tableStatusRow, 0),
}
}
// ShowTables 执行 show tables
func (db *Connector) ShowTables() ([]string, error) {
defer func() {
err := recover()
if err != nil {
common.Log.Error("recover ShowTableStatus()", err)
}
}()
// 执行 show table status
res, err := db.Query("show tables")
if err != nil {
return []string{}, err
}
// 获取值
var tables []string
for _, row := range res.Rows {
tables = append(tables, row.Str(0))
}
return tables, err
}
// ShowTableStatus 执行 show table status
func (db *Connector) ShowTableStatus(tableName string) (*TableStatInfo, error) {
defer func() {
err := recover()
if err != nil {
common.Log.Error("recover ShowTableStatus()", err)
}
}()
// 初始化struct
ts := newTableStat(tableName)
// 执行 show table status
res, err := db.Query("show table status where name = '%s'", ts.Name)
if err != nil {
return ts, err
}
rs := res.Result.Map("Rows")
name := res.Result.Map("Name")
df := res.Result.Map("Data_free")
sum := res.Result.Map("Checksum")
engine := res.Result.Map("Engine")
version := res.Result.Map("Version")
comment := res.Result.Map("Comment")
ai := res.Result.Map("Auto_increment")
collation := res.Result.Map("Collation")
rowFormat := res.Result.Map("Row_format")
checkTime := res.Result.Map("Check_time")
dataLength := res.Result.Map("Data_length")
idxLength := res.Result.Map("Index_length")
createTime := res.Result.Map("Create_time")
updateTime := res.Result.Map("Update_time")
options := res.Result.Map("Create_options")
avgRowLength := res.Result.Map("Avg_row_length")
maxDataLength := res.Result.Map("Max_data_length")
// 获取值
for _, row := range res.Rows {
value := tableStatusRow{
Name: row.Str(name),
Engine: row.Str(engine),
Version: row.Int(version),
Rows: row.Int64(rs),
RowFormat: row.Str(rowFormat),
AvgRowLength: row.Int(avgRowLength),
DataLength: row.Int(dataLength),
MaxDataLength: row.Int(maxDataLength),
IndexLength: row.Int(idxLength),
DataFree: row.Int(df),
AutoIncrement: row.Int(ai),
CreateTime: row.Time(createTime, time.Local),
UpdateTime: row.Time(updateTime, time.Local),
CheckTime: row.Time(checkTime, time.Local),
Collation: row.Str(collation),
Checksum: row.Str(sum),
CreateOptions: row.Str(options),
Comment: row.Str(comment),
}
ts.Rows = append(ts.Rows, value)
}
return ts, err
}
// https://dev.mysql.com/doc/refman/5.7/en/show-index.html
// TableIndexInfo 用以保存 show index 之后获取的 index 信息
type TableIndexInfo struct {
TableName string
IdxRows []TableIndexRow
}
// TableIndexRow 用以存放show index之后获取的每一条index信息
type TableIndexRow struct {
Table string // 表名
NonUnique int // 0:unique key,1:not unique
KeyName string // index的名称,如果是主键则为 "PRIMARY"
SeqInIndex int // 该列在索引中的位置。计数从 1 开始
ColumnName string // 列名
Collation string // A or Null
Cardinality int // 索引中唯一值的数量,"ANALYZE TABLE" 可更新该值
SubPart int // 索引前缀字节数
Packed int
Null string // 表示该列是否可以为空,如果可以为 'YES',反之''
IndexType string // BTREE, FULLTEXT, HASH, RTREE
Comment string
IndexComment string
}
// NewTableIndexInfo 构造 TableIndexInfo
func NewTableIndexInfo(tableName string) *TableIndexInfo {
return &TableIndexInfo{
TableName: tableName,
IdxRows: make([]TableIndexRow, 0),
}
}
// ShowIndex show Index
func (db *Connector) ShowIndex(tableName string) (*TableIndexInfo, error) {
tbIndex := NewTableIndexInfo(tableName)
// 执行 show create table
res, err := db.Query("show index from `%s`.`%s`", db.Database, tableName)
if err != nil {
return nil, err
}
table := res.Result.Map("Table")
unique := res.Result.Map("Non_unique")
keyName := res.Result.Map("Key_name")
seq := res.Result.Map("Seq_in_index")
cName := res.Result.Map("Column_name")
collation := res.Result.Map("Collation")
cardinality := res.Result.Map("Cardinality")
subPart := res.Result.Map("Sub_part")
packed := res.Result.Map("Packed")
null := res.Result.Map("Null")
idxType := res.Result.Map("Index_type")
comment := res.Result.Map("Comment")
idxComment := res.Result.Map("Index_comment")
// 获取值
for _, row := range res.Rows {
value := TableIndexRow{
Table: row.Str(table),
NonUnique: row.Int(unique),
KeyName: row.Str(keyName),
SeqInIndex: row.Int(seq),
ColumnName: row.Str(cName),
Collation: row.Str(collation),
Cardinality: row.Int(cardinality),
SubPart: row.Int(subPart),
Packed: row.Int(packed),
Null: row.Str(null),
IndexType: row.Str(idxType),
Comment: row.Str(comment),
IndexComment: row.Str(idxComment),
}
tbIndex.IdxRows = append(tbIndex.IdxRows, value)
}
return tbIndex, err
}
// IndexSelectKey 用以对 TableIndexInfo 进行查询
type IndexSelectKey string
// 索引相关
const (
IndexKeyName = IndexSelectKey("KeyName") // 索引名称
IndexColumnName = IndexSelectKey("ColumnName") // 索引列名称
IndexIndexType = IndexSelectKey("IndexType") // 索引类型
IndexNonUnique = IndexSelectKey("NonUnique") // 唯一索引
)
// FindIndex 获取TableIndexInfo中需要的索引
func (tbIndex *TableIndexInfo) FindIndex(arg IndexSelectKey, value string) []TableIndexRow {
var result []TableIndexRow
if tbIndex == nil {
return result
}
value = strings.ToLower(value)
switch arg {
case IndexKeyName:
for _, index := range tbIndex.IdxRows {
if strings.ToLower(index.KeyName) == value {
result = append(result, index)
}
}
case IndexColumnName:
for _, index := range tbIndex.IdxRows {
if strings.ToLower(index.ColumnName) == value {
result = append(result, index)
}
}
case IndexIndexType:
for _, index := range tbIndex.IdxRows {
if strings.ToLower(index.IndexType) == value {
result = append(result, index)
}
}
case IndexNonUnique:
for _, index := range tbIndex.IdxRows {
unique := strconv.Itoa(index.NonUnique)
if unique == value {
result = append(result, index)
}
}
default:
common.Log.Error("no such args: TableIndexRow")
}
return result
}
// desc table
// https://dev.mysql.com/doc/refman/5.7/en/show-columns.html
// TableDesc show columns from rental;
type TableDesc struct {
Name string
DescValues []TableDescValue
}
// TableDescValue 含有每一列的属性
type TableDescValue struct {
Field string // 列名
Type string // 数据类型
Null string // 是否有NULL(NO、YES)
Collation string // 字符集
Privileges string // 权限s
Key string // 键类型
Default string // 默认值
Extra string // 其他
Comment string // 备注
}
// NewTableDesc 初始化一个*TableDesc
func NewTableDesc(tableName string) *TableDesc {
return &TableDesc{
Name: tableName,
DescValues: make([]TableDescValue, 0),
}
}
// ShowColumns 获取DB中所有的columns
func (db *Connector) ShowColumns(tableName string) (*TableDesc, error) {
tbDesc := NewTableDesc(tableName)
// 执行 show create table
res, err := db.Query("show full columns from `%s`.`%s`", db.Database, tableName)
if err != nil {
return nil, err
}
field := res.Result.Map("Field")
tp := res.Result.Map("Type")
null := res.Result.Map("Null")
key := res.Result.Map("Key")
def := res.Result.Map("Default")
extra := res.Result.Map("Extra")
collation := res.Result.Map("Collation")
privileges := res.Result.Map("Privileges")
comm := res.Result.Map("Comment")
// 获取值
for _, row := range res.Rows {
value := TableDescValue{
Field: row.Str(field),
Type: row.Str(tp),
Null: row.Str(null),
Key: row.Str(key),
Default: row.Str(def),
Extra: row.Str(extra),
Privileges: row.Str(privileges),
Collation: row.Str(collation),
Comment: row.Str(comm),
}
tbDesc.DescValues = append(tbDesc.DescValues, value)
}
return tbDesc, err
}
// Columns 用于获取TableDesc中所有列的名称
func (td TableDesc) Columns() []string {
var cols []string
for _, col := range td.DescValues {
cols = append(cols, col.Field)
}
return cols
}
// showCreate show create
func (db *Connector) showCreate(createType, name string) (string, error) {
// 执行 show create table
res, err := db.Query("show create %s `%s`", createType, name)
if err != nil {
return "", err
}
// 获取ddl
var ddl string
for _, row := range res.Rows {
ddl = row.Str(1)
}
return ddl, err
}
// ShowCreateDatabase show create database
func (db *Connector) ShowCreateDatabase(dbName string) (string, error) {
defer func() {
err := recover()
if err != nil {
common.Log.Error("recover ShowCreateDatabase()", err)
}
}()
return db.showCreate("database", dbName)
}
// ShowCreateTable show create table
func (db *Connector) ShowCreateTable(tableName string) (string, error) {
defer func() {
err := recover()
if err != nil {
common.Log.Error("recover ShowCreateTable()", err)
}
}()
ddl, err := db.showCreate("table", tableName)
// 去除外键关联条件
var noConstraint []string
relationReg, _ := regexp.Compile("CONSTRAINT")
for _, line := range strings.Split(ddl, "\n") {
if relationReg.Match([]byte(line)) {
continue
}
// 去除外键语句会使DDL中多一个','导致语法错误,要把多余的逗号去除
if strings.Index(line, ")") == 0 {
lineWrongSyntax := noConstraint[len(noConstraint)-1]
// 如果')'前一句的末尾是',' 删除 ',' 保证语法正确性
if strings.Index(lineWrongSyntax, ",") == len(lineWrongSyntax)-1 {
noConstraint[len(noConstraint)-1] = lineWrongSyntax[:len(lineWrongSyntax)-1]
}
}
noConstraint = append(noConstraint, line)
}
return strings.Join(noConstraint, "\n"), err
}
// FindColumn find column
func (db *Connector) FindColumn(name, dbName string, tables ...string) ([]*common.Column, error) {
// 执行 show create table
var columns []*common.Column
sql := fmt.Sprintf("SELECT "+
"c.TABLE_NAME,c.TABLE_SCHEMA,c.COLUMN_TYPE,c.CHARACTER_SET_NAME, c.COLLATION_NAME "+
"FROM `INFORMATION_SCHEMA`.`COLUMNS` as c where c.COLUMN_NAME = '%s' ", name)
if len(tables) > 0 {
var tmp []string
for _, table := range tables {
tmp = append(tmp, "'"+table+"'")
}
sql += fmt.Sprintf(" and c.table_name in (%s)", strings.Join(tmp, ","))
}
if dbName != "" {
sql += fmt.Sprintf(" and c.table_schema = '%s'", dbName)
}
res, err := db.Query(sql)
if err != nil {
common.Log.Error("(db *Connector) FindColumn Error : ", err)
return columns, err
}
tbName := res.Result.Map("TABLE_NAME")
schema := res.Result.Map("TABLE_SCHEMA")
colTyp := res.Result.Map("COLUMN_TYPE")
colCharset := res.Result.Map("CHARACTER_SET_NAME")
collation := res.Result.Map("COLLATION_NAME")
// 获取ddl
for _, row := range res.Rows {
col := &common.Column{
Name: name,
Table: row.Str(tbName),
DB: row.Str(schema),
DataType: row.Str(colTyp),
Character: row.Str(colCharset),
Collation: row.Str(collation),
}
// 填充字符集和排序规则
if col.Character == "" {
// 当从`INFORMATION_SCHEMA`.`COLUMNS`表中查询不到相关列的character和collation的信息时
// 认为该列使用的character和collation与其所处的表一致
// 由于`INFORMATION_SCHEMA`.`TABLES`表中未找到表的character,所以从按照MySQL中collation的规则从中截取character
sql = fmt.Sprintf("SELECT `t`.`TABLE_COLLATION` FROM `INFORMATION_SCHEMA`.`TABLES` AS `t` "+
"WHERE `t`.`TABLE_NAME`='%s' AND `t`.`TABLE_SCHEMA` = '%s'", col.Table, col.DB)
var newRes *QueryResult
newRes, err = db.Query(sql)
if err != nil {
common.Log.Error("(db *Connector) FindColumn Error : ", err)
return columns, err
}
tbCollation := newRes.Rows[0].Str(0)
if tbCollation != "" {
col.Character = strings.Split(tbCollation, "_")[0]
col.Collation = tbCollation
}
}
columns = append(columns, col)
}
return columns, err
}
// IsFKey 判断列是否是外键
func (db *Connector) IsFKey(dbName, tbName, column string) bool {
sql := fmt.Sprintf("SELECT REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE C "+
"WHERE REFERENCED_TABLE_SCHEMA <> 'NULL' AND"+
" TABLE_NAME='%s' AND"+
" TABLE_SCHEMA='%s' AND"+
" COLUMN_NAME='%s'", tbName, dbName, column)
res, err := db.Query(sql)
if err == nil && len(res.Rows) == 0 {
return false
}
return true
}
// Reference 用于存储关系
type Reference map[string][]ReferenceValue
// ReferenceValue 用于处理表之间的关系
type ReferenceValue struct {
RefDBName string // 夫表所属数据库
RefTable string // 父表
DBName string // 子表所属数据库
Table string // 子表
ConstraintName string // 关系名称
}
// ShowReference 查找所有的外键信息
func (db *Connector) ShowReference(dbName string, tbName ...string) ([]ReferenceValue, error) {
var referenceValues []ReferenceValue
sql := `SELECT C.REFERENCED_TABLE_SCHEMA,C.REFERENCED_TABLE_NAME,C.TABLE_SCHEMA,C.TABLE_NAME,C.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE C JOIN INFORMATION_SCHEMA. TABLES T ON T.TABLE_NAME = C.TABLE_NAME
WHERE C.REFERENCED_TABLE_NAME IS NOT NULL`
sql = sql + fmt.Sprintf(` AND C.TABLE_SCHEMA = "%s"`, dbName)
if len(tbName) > 0 {
extra := fmt.Sprintf(` AND C.TABLE_NAME IN ("%s")`, strings.Join(tbName, `","`))
sql = sql + extra
}
// 执行SQL查找外键关联关系
res, err := db.Query(sql)
if err != nil {
return referenceValues, err
}
refDb := res.Result.Map("REFERENCED_TABLE_SCHEMA")
refTb := res.Result.Map("REFERENCED_TABLE_NAME")
schema := res.Result.Map("TABLE_SCHEMA")
tb := res.Result.Map("TABLE_NAME")
cName := res.Result.Map("CONSTRAINT_NAME")
// 获取值
for _, row := range res.Rows {
value := ReferenceValue{
RefDBName: row.Str(refDb),
RefTable: row.Str(refTb),
DBName: row.Str(schema),
Table: row.Str(tb),
ConstraintName: row.Str(cName),
}
referenceValues = append(referenceValues, value)
}
return referenceValues, err
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。