代码拉取完成,页面将自动刷新
/*
* 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 (
"errors"
"fmt"
"io"
"regexp"
"strings"
"time"
"github.com/XiaoMi/soar/common"
"vitess.io/vitess/go/vt/sqlparser"
)
// Trace 用于存放 Select * From Information_Schema.Optimizer_Trace;输出的结果
type Trace struct {
Rows []TraceRow
}
// TraceRow 中含有trace的基本信息
type TraceRow struct {
Query string
Trace string
MissingBytesBeyondMaxMemSize int
InsufficientPrivileges int
}
// Trace 执行SQL,并对其Trace
func (db *Connector) Trace(sql string, params ...interface{}) (*QueryResult, error) {
common.Log.Debug("Trace SQL: %s", sql)
if common.Config.TestDSN.Version < 560 {
return nil, errors.New("version < 5.6, not support trace")
}
// 过滤不需要Trace的SQL
switch sqlparser.Preview(sql) {
case sqlparser.StmtSelect, sqlparser.StmtUpdate, sqlparser.StmtDelete:
sql = "explain " + sql
case sqlparser.EXPLAIN:
default:
return nil, errors.New("no need trace")
}
// 测试环境如果检查是关闭的,则SQL不会被执行
if common.Config.TestDSN.Disable {
return nil, errors.New("TestDsn Disable")
}
// 数据库安全性检查:如果Connector的IP端口与TEST环境不一致,则启用SQL白名单
// 不在白名单中的SQL不允许执行
// 执行环境与test环境不相同
if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) {
return nil, fmt.Errorf("query Execution Deny: Execute SQL with DSN(%s/%s) '%s'",
db.Addr, db.Database, fmt.Sprintf(sql, params...))
}
common.Log.Debug("Execute SQL with DSN(%s/%s) : %s", db.Addr, db.Database, sql)
conn := db.NewConnection()
// 设置SQL连接超时时间
conn.SetTimeout(time.Duration(common.Config.ConnTimeOut) * time.Second)
defer conn.Close()
err := conn.Connect()
if err != nil {
return nil, err
}
// 添加SQL执行超时限制
ch := make(chan QueryResult, 1)
go func() {
// 开启Trace
common.Log.Debug("SET SESSION OPTIMIZER_TRACE='enabled=on'")
_, _, err = conn.Query("SET SESSION OPTIMIZER_TRACE='enabled=on'")
common.LogIfError(err, "")
// 执行SQL,抛弃返回结果
result, err := conn.Start(sql, params...)
if err != nil {
ch <- QueryResult{
Error: err,
}
return
}
row := result.MakeRow()
for {
err = result.ScanRow(row)
if err == io.EOF {
break
}
}
// 返回Trace结果
res := QueryResult{}
res.Rows, res.Result, res.Error = conn.Query("SELECT * FROM information_schema.OPTIMIZER_TRACE")
// 关闭Trace
common.Log.Debug("SET SESSION OPTIMIZER_TRACE='enabled=off'")
_, _, err = conn.Query("SET SESSION OPTIMIZER_TRACE='enabled=off'")
if err != nil {
fmt.Println(err.Error())
}
ch <- res
}()
select {
case res := <-ch:
return &res, res.Error
case <-time.After(time.Duration(common.Config.QueryTimeOut) * time.Second):
return nil, errors.New("query execution timeout")
}
}
// getTrace 获取trace信息
func getTrace(res *QueryResult) Trace {
var rows []TraceRow
for _, row := range res.Rows {
rows = append(rows, TraceRow{
Query: row.Str(0),
Trace: row.Str(1),
MissingBytesBeyondMaxMemSize: row.Int(2),
InsufficientPrivileges: row.Int(3),
})
}
return Trace{Rows: rows}
}
// FormatTrace 格式化输出Trace信息
func FormatTrace(res *QueryResult) string {
explainReg := regexp.MustCompile(`(?i)^explain\s+`)
trace := getTrace(res)
str := []string{""}
for _, row := range trace.Rows {
str = append(str, "```sql")
sql := explainReg.ReplaceAllString(row.Query, "")
str = append(str, sql)
str = append(str, "```\n")
str = append(str, "```json")
str = append(str, row.Trace)
str = append(str, "```\n")
}
return strings.Join(str, "\n")
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。