Skip to content

Commit 1d91121

Browse files
qw4990ti-chi-bot
authored andcommitted
This is an automated cherry-pick of pingcap#58017
Signed-off-by: ti-chi-bot <[email protected]>
1 parent ee67770 commit 1d91121

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed

pkg/bindinfo/BUILD.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,15 @@ go_library(
3636
"//pkg/util/sqlexec",
3737
"//pkg/util/stmtsummary/v2:stmtsummary",
3838
"//pkg/util/table-filter",
39+
<<<<<<< HEAD
3940
"//pkg/util/timeutil",
4041
"@org_golang_x_exp//maps",
42+
=======
43+
"@com_github_pingcap_errors//:errors",
44+
"@com_github_pingcap_failpoint//:failpoint",
45+
"@com_github_pkg_errors//:errors",
46+
"@org_golang_x_sync//singleflight",
47+
>>>>>>> 821563f2983 (planner: handle panic when loading bindings at startup (#58017))
4148
"@org_uber_go_zap//:zap",
4249
],
4350
)

pkg/bindinfo/binding.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// Copyright 2019 PingCAP, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package bindinfo
16+
17+
import (
18+
"time"
19+
"unsafe"
20+
21+
"github.com/pingcap/tidb/pkg/parser"
22+
"github.com/pingcap/tidb/pkg/parser/ast"
23+
"github.com/pingcap/tidb/pkg/sessionctx"
24+
"github.com/pingcap/tidb/pkg/types"
25+
"github.com/pingcap/tidb/pkg/util/hint"
26+
"github.com/pkg/errors"
27+
)
28+
29+
const (
30+
// Enabled is the bind info's in enabled status.
31+
// It is the same as the previous 'Using' status.
32+
// Only use 'Enabled' status in the future, not the 'Using' status.
33+
// The 'Using' status is preserved for compatibility.
34+
Enabled = "enabled"
35+
// Disabled is the bind info's in disabled status.
36+
Disabled = "disabled"
37+
// Using is the bind info's in use status.
38+
// The 'Using' status is preserved for compatibility.
39+
Using = "using"
40+
// deleted is the bind info's deleted status.
41+
deleted = "deleted"
42+
// Invalid is the bind info's invalid status.
43+
Invalid = "invalid"
44+
// Manual indicates the binding is created by SQL like "create binding for ...".
45+
Manual = "manual"
46+
// Capture indicates the binding is captured by TiDB automatically.
47+
Capture = "capture"
48+
// Builtin indicates the binding is a builtin record for internal locking purpose. It is also the status for the builtin binding.
49+
Builtin = "builtin"
50+
// History indicate the binding is created from statement summary by plan digest
51+
History = "history"
52+
)
53+
54+
// Binding stores the basic bind hint info.
55+
type Binding struct {
56+
OriginalSQL string
57+
Db string
58+
BindSQL string
59+
// Status represents the status of the binding. It can only be one of the following values:
60+
// 1. deleted: Bindings is deleted, can not be used anymore.
61+
// 2. enabled, using: Binding is in the normal active mode.
62+
Status string
63+
CreateTime types.Time
64+
UpdateTime types.Time
65+
Source string
66+
Charset string
67+
Collation string
68+
// Hint is the parsed hints, it is used to bind hints to stmt node.
69+
Hint *hint.HintsSet `json:"-"`
70+
// ID is the string form of Hint. It would be non-empty only when the status is `Using` or `PendingVerify`.
71+
ID string `json:"-"`
72+
SQLDigest string
73+
PlanDigest string
74+
75+
// TableNames records all schema and table names in this binding statement, which are used for fuzzy matching.
76+
TableNames []*ast.TableName `json:"-"`
77+
}
78+
79+
func (b *Binding) isSame(rb *Binding) bool {
80+
if b.ID != "" && rb.ID != "" {
81+
return b.ID == rb.ID
82+
}
83+
// Sometimes we cannot construct `ID` because of the changed schema, so we need to compare by bind sql.
84+
return b.BindSQL == rb.BindSQL
85+
}
86+
87+
// IsBindingEnabled returns whether the binding is enabled.
88+
func (b *Binding) IsBindingEnabled() bool {
89+
return b.Status == Enabled || b.Status == Using
90+
}
91+
92+
// IsBindingAvailable returns whether the binding is available.
93+
// The available means the binding can be used or can be converted into a usable status.
94+
// It includes the 'Enabled', 'Using' and 'Disabled' status.
95+
func (b *Binding) IsBindingAvailable() bool {
96+
return b.IsBindingEnabled() || b.Status == Disabled
97+
}
98+
99+
// SinceUpdateTime returns the duration since last update time. Export for test.
100+
func (b *Binding) SinceUpdateTime() (time.Duration, error) {
101+
updateTime, err := b.UpdateTime.GoTime(time.Local)
102+
if err != nil {
103+
return 0, err
104+
}
105+
return time.Since(updateTime), nil
106+
}
107+
108+
// Bindings represents a sql bind record retrieved from the storage.
109+
type Bindings []Binding
110+
111+
// Copy get the copy of bindings
112+
func (br Bindings) Copy() Bindings {
113+
nbr := append(make(Bindings, 0, len(br)), br...)
114+
return nbr
115+
}
116+
117+
// HasAvailableBinding checks if there are any available bindings in bind record.
118+
// The available means the binding can be used or can be converted into a usable status.
119+
// It includes the 'Enabled', 'Using' and 'Disabled' status.
120+
func HasAvailableBinding(br Bindings) bool {
121+
if br == nil {
122+
return false
123+
}
124+
for _, binding := range br {
125+
if binding.IsBindingAvailable() {
126+
return true
127+
}
128+
}
129+
return false
130+
}
131+
132+
// prepareHints builds ID and Hint for Bindings. If sctx is not nil, we check if
133+
// the BindSQL is still valid.
134+
func prepareHints(sctx sessionctx.Context, binding *Binding) (rerr error) {
135+
defer func() {
136+
if r := recover(); r != nil {
137+
rerr = errors.Errorf("panic when preparing hints for binding %v, panic: %v", binding.BindSQL, r)
138+
}
139+
}()
140+
141+
p := parser.New()
142+
if (binding.Hint != nil && binding.ID != "") || binding.Status == deleted {
143+
return nil
144+
}
145+
dbName := binding.Db
146+
bindingStmt, err := p.ParseOneStmt(binding.BindSQL, binding.Charset, binding.Collation)
147+
if err != nil {
148+
return err
149+
}
150+
tableNames := CollectTableNames(bindingStmt)
151+
isFuzzy := isFuzzyBinding(bindingStmt)
152+
if isFuzzy {
153+
dbName = "*" // ues '*' for universal bindings
154+
}
155+
156+
hintsSet, stmt, warns, err := hint.ParseHintsSet(p, binding.BindSQL, binding.Charset, binding.Collation, dbName)
157+
if err != nil {
158+
return err
159+
}
160+
if sctx != nil && !isFuzzy {
161+
paramChecker := &paramMarkerChecker{}
162+
stmt.Accept(paramChecker)
163+
if !paramChecker.hasParamMarker {
164+
_, err = getHintsForSQL(sctx, binding.BindSQL)
165+
if err != nil {
166+
return err
167+
}
168+
}
169+
}
170+
hintsStr, err := hintsSet.Restore()
171+
if err != nil {
172+
return err
173+
}
174+
// For `create global binding for select * from t using select * from t`, we allow it though hintsStr is empty.
175+
// For `create global binding for select * from t using select /*+ non_exist_hint() */ * from t`,
176+
// the hint is totally invalid, we escalate warning to error.
177+
if hintsStr == "" && len(warns) > 0 {
178+
return warns[0]
179+
}
180+
binding.Hint = hintsSet
181+
binding.ID = hintsStr
182+
binding.TableNames = tableNames
183+
return nil
184+
}
185+
186+
// `merge` merges two Bindings. It will replace old bindings with new bindings if there are new updates.
187+
func merge(lBindings, rBindings Bindings) Bindings {
188+
if lBindings == nil {
189+
return rBindings
190+
}
191+
if rBindings == nil {
192+
return lBindings
193+
}
194+
result := lBindings.Copy()
195+
for i := range rBindings {
196+
rbind := rBindings[i]
197+
found := false
198+
for j, lbind := range lBindings {
199+
if lbind.isSame(&rbind) {
200+
found = true
201+
if rbind.UpdateTime.Compare(lbind.UpdateTime) >= 0 {
202+
result[j] = rbind
203+
}
204+
break
205+
}
206+
}
207+
if !found {
208+
result = append(result, rbind)
209+
}
210+
}
211+
return result
212+
}
213+
214+
func removeDeletedBindings(br Bindings) Bindings {
215+
result := make(Bindings, 0, len(br))
216+
for _, binding := range br {
217+
if binding.Status != deleted {
218+
result = append(result, binding)
219+
}
220+
}
221+
return result
222+
}
223+
224+
// size calculates the memory size of a Bindings.
225+
func (br Bindings) size() float64 {
226+
mem := float64(0)
227+
for _, binding := range br {
228+
mem += binding.size()
229+
}
230+
return mem
231+
}
232+
233+
// size calculates the memory size of a bind info.
234+
func (b *Binding) size() float64 {
235+
res := len(b.OriginalSQL) + len(b.Db) + len(b.BindSQL) + len(b.Status) + 2*int(unsafe.Sizeof(b.CreateTime)) + len(b.Charset) + len(b.Collation) + len(b.ID)
236+
return float64(res)
237+
}

0 commit comments

Comments
 (0)