github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/executor/prepared.go (about) 1 // Copyright 2015 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package executor 15 16 import ( 17 "sort" 18 19 "github.com/insionng/yougam/libraries/juju/errors" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/context" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/evaluator" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/infoschema" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/optimizer" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/optimizer/plan" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/parser" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx" 28 "github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable" 29 ) 30 31 var ( 32 _ Executor = &DeallocateExec{} 33 _ Executor = &ExecuteExec{} 34 _ Executor = &PrepareExec{} 35 ) 36 37 type paramMarkerSorter struct { 38 markers []*ast.ParamMarkerExpr 39 } 40 41 func (p *paramMarkerSorter) Len() int { 42 return len(p.markers) 43 } 44 45 func (p *paramMarkerSorter) Less(i, j int) bool { 46 return p.markers[i].Offset < p.markers[j].Offset 47 } 48 49 func (p *paramMarkerSorter) Swap(i, j int) { 50 p.markers[i], p.markers[j] = p.markers[j], p.markers[i] 51 } 52 53 type paramMarkerExtractor struct { 54 markers []*ast.ParamMarkerExpr 55 } 56 57 func (e *paramMarkerExtractor) Enter(in ast.Node) (ast.Node, bool) { 58 return in, false 59 } 60 61 func (e *paramMarkerExtractor) Leave(in ast.Node) (ast.Node, bool) { 62 if x, ok := in.(*ast.ParamMarkerExpr); ok { 63 e.markers = append(e.markers, x) 64 } 65 return in, true 66 } 67 68 // Prepared represents a prepared statement. 69 type Prepared struct { 70 Stmt ast.StmtNode 71 Params []*ast.ParamMarkerExpr 72 SchemaVersion int64 73 } 74 75 // PrepareExec represents a PREPARE executor. 76 type PrepareExec struct { 77 IS infoschema.InfoSchema 78 Ctx context.Context 79 Name string 80 SQLText string 81 82 ID uint32 83 ResultFields []*ast.ResultField 84 ParamCount int 85 Err error 86 } 87 88 // Fields implements Executor Fields interface. 89 func (e *PrepareExec) Fields() []*ast.ResultField { 90 // returns nil to indicate prepare will not return Recordset. 91 return nil 92 } 93 94 // Next implements Executor Next interface. 95 func (e *PrepareExec) Next() (*Row, error) { 96 e.DoPrepare() 97 return nil, e.Err 98 } 99 100 // Close implements plan.Plan Close interface. 101 func (e *PrepareExec) Close() error { 102 return nil 103 } 104 105 // DoPrepare prepares the statement, it can be called multiple times without 106 // side effect. 107 func (e *PrepareExec) DoPrepare() { 108 vars := variable.GetSessionVars(e.Ctx) 109 if e.ID != 0 { 110 // Must be the case when we retry a prepare. 111 // Make sure it is idempotent. 112 _, ok := vars.PreparedStmts[e.ID] 113 if ok { 114 return 115 } 116 } 117 charset, collation := variable.GetCharsetInfo(e.Ctx) 118 stmts, err := parser.Parse(e.SQLText, charset, collation) 119 if err != nil { 120 e.Err = errors.Trace(err) 121 return 122 } 123 if len(stmts) != 1 { 124 e.Err = ErrPrepareMulti 125 return 126 } 127 stmt := stmts[0] 128 var extractor paramMarkerExtractor 129 stmt.Accept(&extractor) 130 131 // The parameter markers are appended in visiting order, which may not 132 // be the same as the position order in the query string. We need to 133 // sort it by position. 134 sorter := ¶mMarkerSorter{markers: extractor.markers} 135 sort.Sort(sorter) 136 e.ParamCount = len(sorter.markers) 137 prepared := &Prepared{ 138 Stmt: stmt, 139 Params: sorter.markers, 140 SchemaVersion: e.IS.SchemaMetaVersion(), 141 } 142 143 err = optimizer.Prepare(e.IS, e.Ctx, stmt) 144 if err != nil { 145 e.Err = errors.Trace(err) 146 return 147 } 148 if resultSetNode, ok := stmt.(ast.ResultSetNode); ok { 149 e.ResultFields = resultSetNode.GetResultFields() 150 } 151 152 if e.ID == 0 { 153 e.ID = vars.GetNextPreparedStmtID() 154 } 155 if e.Name != "" { 156 vars.PreparedStmtNameToID[e.Name] = e.ID 157 } 158 vars.PreparedStmts[e.ID] = prepared 159 } 160 161 // ExecuteExec represents an EXECUTE executor. 162 // It executes a prepared statement. 163 type ExecuteExec struct { 164 IS infoschema.InfoSchema 165 Ctx context.Context 166 Name string 167 UsingVars []ast.ExprNode 168 ID uint32 169 StmtExec Executor 170 Stmt ast.StmtNode 171 } 172 173 // Fields implements Executor Fields interface. 174 func (e *ExecuteExec) Fields() []*ast.ResultField { 175 // Will never be called. 176 return nil 177 } 178 179 // Next implements Executor Next interface. 180 func (e *ExecuteExec) Next() (*Row, error) { 181 // Will never be called. 182 return nil, nil 183 } 184 185 // Close implements plan.Plan Close interface. 186 func (e *ExecuteExec) Close() error { 187 // Will never be called. 188 return nil 189 } 190 191 // Build builds a prepared statement into an executor. 192 func (e *ExecuteExec) Build() error { 193 vars := variable.GetSessionVars(e.Ctx) 194 if e.Name != "" { 195 e.ID = vars.PreparedStmtNameToID[e.Name] 196 } 197 v := vars.PreparedStmts[e.ID] 198 if v == nil { 199 return ErrStmtNotFound 200 } 201 prepared := v.(*Prepared) 202 203 if len(prepared.Params) != len(e.UsingVars) { 204 return ErrWrongParamCount 205 } 206 207 for i, usingVar := range e.UsingVars { 208 val, err := evaluator.Eval(e.Ctx, usingVar) 209 if err != nil { 210 return errors.Trace(err) 211 } 212 prepared.Params[i].SetDatum(val) 213 } 214 215 ast.ResetEvaluatedFlag(prepared.Stmt) 216 if prepared.SchemaVersion != e.IS.SchemaMetaVersion() { 217 // If the schema version has changed we need to prepare it again, 218 // if this time it failed, the real reason for the error is schema changed. 219 err := optimizer.Prepare(e.IS, e.Ctx, prepared.Stmt) 220 if err != nil { 221 return ErrSchemaChanged.Gen("Schema change casued error: %s", err.Error()) 222 } 223 prepared.SchemaVersion = e.IS.SchemaMetaVersion() 224 } 225 sb := &subqueryBuilder{is: e.IS} 226 plan, err := optimizer.Optimize(e.Ctx, prepared.Stmt, sb) 227 if err != nil { 228 return errors.Trace(err) 229 } 230 b := newExecutorBuilder(e.Ctx, e.IS) 231 stmtExec := b.build(plan) 232 if b.err != nil { 233 return errors.Trace(b.err) 234 } 235 e.StmtExec = stmtExec 236 e.Stmt = prepared.Stmt 237 return nil 238 } 239 240 // DeallocateExec represent a DEALLOCATE executor. 241 type DeallocateExec struct { 242 Name string 243 ctx context.Context 244 } 245 246 // Fields implements Executor Fields interface. 247 func (e *DeallocateExec) Fields() []*ast.ResultField { 248 return nil 249 } 250 251 // Next implements Executor Next interface. 252 func (e *DeallocateExec) Next() (*Row, error) { 253 vars := variable.GetSessionVars(e.ctx) 254 id, ok := vars.PreparedStmtNameToID[e.Name] 255 if !ok { 256 return nil, ErrStmtNotFound 257 } 258 delete(vars.PreparedStmtNameToID, e.Name) 259 delete(vars.PreparedStmts, id) 260 return nil, nil 261 } 262 263 // Close implements plan.Plan Close interface. 264 func (e *DeallocateExec) Close() error { 265 return nil 266 } 267 268 // CompileExecutePreparedStmt compiles a session Execute command to a stmt.Statement. 269 func CompileExecutePreparedStmt(ctx context.Context, ID uint32, args ...interface{}) ast.Statement { 270 execPlan := &plan.Execute{ID: ID} 271 execPlan.UsingVars = make([]ast.ExprNode, len(args)) 272 for i, val := range args { 273 execPlan.UsingVars[i] = ast.NewValueExpr(val) 274 } 275 sa := &statement{ 276 is: sessionctx.GetDomain(ctx).InfoSchema(), 277 plan: execPlan, 278 } 279 return sa 280 }