-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
query.go
131 lines (119 loc) · 4.04 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2014, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mysqlctl
import (
"fmt"
"strings"
"time"
log "github.com/golang/glog"
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/dbconnpool"
)
// getPoolReconnect gets a connection from a pool, tests it, and reconnects if
// it gets errno 2006.
func getPoolReconnect(pool *dbconnpool.ConnectionPool, timeout time.Duration) (dbconnpool.PoolConnection, error) {
conn, err := pool.Get(timeout)
if err != nil {
return conn, err
}
// Run a test query to see if this connection is still good.
if _, err := conn.ExecuteFetch("SELECT 1", 1, false); err != nil {
// If we get "MySQL server has gone away (errno 2006)", try to reconnect.
if sqlErr, ok := err.(*sqldb.SQLError); ok && sqlErr.Number() == 2006 {
if err := conn.Reconnect(); err != nil {
conn.Recycle()
return conn, err
}
}
}
return conn, nil
}
// ExecuteSuperQuery allows the user to execute a query as a super user.
func (mysqld *Mysqld) ExecuteSuperQuery(query string) error {
return mysqld.ExecuteSuperQueryList([]string{query})
}
// ExecuteSuperQueryList alows the user to execute queries as a super user.
func (mysqld *Mysqld) ExecuteSuperQueryList(queryList []string) error {
conn, connErr := getPoolReconnect(mysqld.dbaPool, 0)
if connErr != nil {
return connErr
}
defer conn.Recycle()
for _, query := range queryList {
log.Infof("exec %v", redactMasterPassword(query))
if _, err := conn.ExecuteFetch(query, 10000, false); err != nil {
return fmt.Errorf("ExecuteFetch(%v) failed: %v", redactMasterPassword(query), err.Error())
}
}
return nil
}
// FetchSuperQuery returns the results of executing a query as a super user.
func (mysqld *Mysqld) FetchSuperQuery(query string) (*sqltypes.Result, error) {
conn, connErr := getPoolReconnect(mysqld.dbaPool, 0)
if connErr != nil {
return nil, connErr
}
defer conn.Recycle()
log.V(6).Infof("fetch %v", query)
qr, err := conn.ExecuteFetch(query, 10000, true)
if err != nil {
return nil, err
}
return qr, nil
}
// fetchSuperQueryMap returns a map from column names to cell data for a query
// that should return either 0 or 1 row. If the query returns zero rows, this
// will return a nil map and nil error.
func (mysqld *Mysqld) fetchSuperQueryMap(query string) (map[string]string, error) {
qr, err := mysqld.FetchSuperQuery(query)
if err != nil {
return nil, err
}
if len(qr.Rows) == 0 {
// The query succeeded, but there is no data.
return nil, nil
}
if len(qr.Rows) > 1 {
return nil, fmt.Errorf("query %#v returned %d rows, expected 1", query, len(qr.Rows))
}
if len(qr.Fields) != len(qr.Rows[0]) {
return nil, fmt.Errorf("query %#v returned %d column names, expected %d", query, len(qr.Fields), len(qr.Rows[0]))
}
rowMap := make(map[string]string, len(qr.Rows[0]))
for i, value := range qr.Rows[0] {
rowMap[qr.Fields[i].Name] = value.String()
}
return rowMap, nil
}
// fetchVariables returns a map from MySQL variable names to variable value
// for variables that match the given pattern.
func (mysqld *Mysqld) fetchVariables(pattern string) (map[string]string, error) {
query := fmt.Sprintf("SHOW VARIABLES LIKE '%s'", pattern)
qr, err := mysqld.FetchSuperQuery(query)
if err != nil {
return nil, err
}
if len(qr.Fields) != 2 {
return nil, fmt.Errorf("query %#v returned %d columns, expected 2", query, len(qr.Fields))
}
varMap := make(map[string]string, len(qr.Rows))
for _, row := range qr.Rows {
varMap[row[0].String()] = row[1].String()
}
return varMap, nil
}
const masterPasswordStart = " MASTER_PASSWORD = '"
const masterPasswordEnd = "',\n"
func redactMasterPassword(input string) string {
i := strings.Index(input, masterPasswordStart)
if i == -1 {
return input
}
j := strings.Index(input[i+len(masterPasswordStart):], masterPasswordEnd)
if j == -1 {
return input
}
return input[:i+len(masterPasswordStart)] + strings.Repeat("*", j) + input[i+len(masterPasswordStart)+j:]
}