github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/prepared.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 interlock 15 16 import ( 17 "context" 18 "math" 19 "sort" 20 "time" 21 22 "github.com/whtcorpsinc/BerolinaSQL" 23 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 24 "github.com/whtcorpsinc/BerolinaSQL/ast" 25 "github.com/whtcorpsinc/errors" 26 "github.com/whtcorpsinc/log" 27 "github.com/whtcorpsinc/milevadb/causet" 28 causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded" 29 "github.com/whtcorpsinc/milevadb/memex" 30 "github.com/whtcorpsinc/milevadb/schemareplicant" 31 "github.com/whtcorpsinc/milevadb/soliton" 32 "github.com/whtcorpsinc/milevadb/soliton/chunk" 33 "github.com/whtcorpsinc/milevadb/soliton/hint" 34 "github.com/whtcorpsinc/milevadb/soliton/sqlexec" 35 "github.com/whtcorpsinc/milevadb/stochastikctx" 36 "github.com/whtcorpsinc/milevadb/types" 37 driver "github.com/whtcorpsinc/milevadb/types/BerolinaSQL_driver" 38 "go.uber.org/zap" 39 ) 40 41 var ( 42 _ InterlockingDirectorate = &DeallocateInterDirc{} 43 _ InterlockingDirectorate = &InterDircuteInterDirc{} 44 _ InterlockingDirectorate = &PrepareInterDirc{} 45 ) 46 47 type paramMarkerSorter struct { 48 markers []ast.ParamMarkerExpr 49 } 50 51 func (p *paramMarkerSorter) Len() int { 52 return len(p.markers) 53 } 54 55 func (p *paramMarkerSorter) Less(i, j int) bool { 56 return p.markers[i].(*driver.ParamMarkerExpr).Offset < p.markers[j].(*driver.ParamMarkerExpr).Offset 57 } 58 59 func (p *paramMarkerSorter) Swap(i, j int) { 60 p.markers[i], p.markers[j] = p.markers[j], p.markers[i] 61 } 62 63 type paramMarkerExtractor struct { 64 markers []ast.ParamMarkerExpr 65 } 66 67 func (e *paramMarkerExtractor) Enter(in ast.Node) (ast.Node, bool) { 68 return in, false 69 } 70 71 func (e *paramMarkerExtractor) Leave(in ast.Node) (ast.Node, bool) { 72 if x, ok := in.(*driver.ParamMarkerExpr); ok { 73 e.markers = append(e.markers, x) 74 } 75 return in, true 76 } 77 78 // PrepareInterDirc represents a PREPARE interlock. 79 type PrepareInterDirc struct { 80 baseInterlockingDirectorate 81 82 is schemareplicant.SchemaReplicant 83 name string 84 sqlText string 85 86 ID uint32 87 ParamCount int 88 Fields []*ast.ResultField 89 } 90 91 // NewPrepareInterDirc creates a new PrepareInterDirc. 92 func NewPrepareInterDirc(ctx stochastikctx.Context, is schemareplicant.SchemaReplicant, sqlTxt string) *PrepareInterDirc { 93 base := newBaseInterlockingDirectorate(ctx, nil, 0) 94 base.initCap = chunk.ZeroCapacity 95 return &PrepareInterDirc{ 96 baseInterlockingDirectorate: base, 97 is: is, 98 sqlText: sqlTxt, 99 } 100 } 101 102 // Next implements the InterlockingDirectorate Next interface. 103 func (e *PrepareInterDirc) Next(ctx context.Context, req *chunk.Chunk) error { 104 vars := e.ctx.GetStochastikVars() 105 if e.ID != 0 { 106 // Must be the case when we retry a prepare. 107 // Make sure it is idempotent. 108 _, ok := vars.PreparedStmts[e.ID] 109 if ok { 110 return nil 111 } 112 } 113 charset, defCauslation := vars.GetCharsetInfo() 114 var ( 115 stmts []ast.StmtNode 116 err error 117 ) 118 if sqlBerolinaSQL, ok := e.ctx.(sqlexec.ALLEGROSQLBerolinaSQL); ok { 119 stmts, err = sqlBerolinaSQL.ParseALLEGROSQL(e.sqlText, charset, defCauslation) 120 } else { 121 p := BerolinaSQL.New() 122 p.EnableWindowFunc(vars.EnableWindowFunction) 123 var warns []error 124 stmts, warns, err = p.Parse(e.sqlText, charset, defCauslation) 125 for _, warn := range warns { 126 e.ctx.GetStochastikVars().StmtCtx.AppendWarning(soliton.SyntaxWarn(warn)) 127 } 128 } 129 if err != nil { 130 return soliton.SyntaxError(err) 131 } 132 if len(stmts) != 1 { 133 return ErrPrepareMulti 134 } 135 stmt := stmts[0] 136 137 err = ResetContextOfStmt(e.ctx, stmt) 138 if err != nil { 139 return err 140 } 141 142 var extractor paramMarkerExtractor 143 stmt.Accept(&extractor) 144 145 // DBS Statements can not accept parameters 146 if _, ok := stmt.(ast.DBSNode); ok && len(extractor.markers) > 0 { 147 return ErrPrepareDBS 148 } 149 150 // Prepare parameters should NOT over 2 bytes(MaxUint16) 151 // https://dev.allegrosql.com/doc/internals/en/com-stmt-prepare-response.html#packet-COM_STMT_PREPARE_OK. 152 if len(extractor.markers) > math.MaxUint16 { 153 return ErrPsManyParam 154 } 155 156 err = causetembedded.Preprocess(e.ctx, stmt, e.is, causetembedded.InPrepare) 157 if err != nil { 158 return err 159 } 160 161 // The parameter markers are appended in visiting order, which may not 162 // be the same as the position order in the query string. We need to 163 // sort it by position. 164 sorter := ¶mMarkerSorter{markers: extractor.markers} 165 sort.Sort(sorter) 166 e.ParamCount = len(sorter.markers) 167 for i := 0; i < e.ParamCount; i++ { 168 sorter.markers[i].SetOrder(i) 169 } 170 prepared := &ast.Prepared{ 171 Stmt: stmt, 172 StmtType: GetStmtLabel(stmt), 173 Params: sorter.markers, 174 SchemaVersion: e.is.SchemaMetaVersion(), 175 } 176 177 if !causetembedded.PreparedCausetCacheEnabled() { 178 prepared.UseCache = false 179 } else { 180 if !e.ctx.GetStochastikVars().UseDynamicPartitionPrune() { 181 prepared.UseCache = causetembedded.Cacheable(stmt, e.is) 182 } else { 183 prepared.UseCache = causetembedded.Cacheable(stmt, nil) 184 } 185 } 186 187 // We try to build the real memex of preparedStmt. 188 for i := range prepared.Params { 189 param := prepared.Params[i].(*driver.ParamMarkerExpr) 190 param.Causet.SetNull() 191 param.InInterDircute = false 192 } 193 var p causetembedded.Causet 194 e.ctx.GetStochastikVars().CausetID = 0 195 e.ctx.GetStochastikVars().CausetDeferredCausetID = 0 196 destBuilder := causetembedded.NewCausetBuilder(e.ctx, e.is, &hint.BlockHintProcessor{}) 197 p, err = destBuilder.Build(ctx, stmt) 198 if err != nil { 199 return err 200 } 201 if _, ok := stmt.(*ast.SelectStmt); ok { 202 e.Fields = defCausNames2ResultFields(p.Schema(), p.OutputNames(), vars.CurrentDB) 203 } 204 if e.ID == 0 { 205 e.ID = vars.GetNextPreparedStmtID() 206 } 207 if e.name != "" { 208 vars.PreparedStmtNameToID[e.name] = e.ID 209 } 210 211 normalized, digest := BerolinaSQL.NormalizeDigest(prepared.Stmt.Text()) 212 preparedObj := &causetembedded.CachedPrepareStmt{ 213 PreparedAst: prepared, 214 VisitInfos: destBuilder.GetVisitInfo(), 215 NormalizedALLEGROSQL: normalized, 216 ALLEGROSQLDigest: digest, 217 } 218 return vars.AddPreparedStmt(e.ID, preparedObj) 219 } 220 221 // InterDircuteInterDirc represents an EXECUTE interlock. 222 // It cannot be executed by itself, all it needs to do is to build 223 // another InterlockingDirectorate from a prepared memex. 224 type InterDircuteInterDirc struct { 225 baseInterlockingDirectorate 226 227 is schemareplicant.SchemaReplicant 228 name string 229 usingVars []memex.Expression 230 stmtInterDirc InterlockingDirectorate 231 stmt ast.StmtNode 232 plan causetembedded.Causet 233 id uint32 234 lowerPriority bool 235 outputNames []*types.FieldName 236 } 237 238 // Next implements the InterlockingDirectorate Next interface. 239 func (e *InterDircuteInterDirc) Next(ctx context.Context, req *chunk.Chunk) error { 240 return nil 241 } 242 243 // Build builds a prepared memex into an interlock. 244 // After Build, e.StmtInterDirc will be used to do the real execution. 245 func (e *InterDircuteInterDirc) Build(b *interlockBuilder) error { 246 ok, err := causetembedded.IsPointGetWithPKOrUniqueKeyByAutoCommit(e.ctx, e.plan) 247 if err != nil { 248 return err 249 } 250 if ok { 251 err = e.ctx.InitTxnWithStartTS(math.MaxUint64) 252 } 253 if err != nil { 254 return err 255 } 256 stmtInterDirc := b.build(e.plan) 257 if b.err != nil { 258 log.Warn("rebuild plan in EXECUTE memex failed", zap.String("labelName of PREPARE memex", e.name)) 259 return errors.Trace(b.err) 260 } 261 e.stmtInterDirc = stmtInterDirc 262 if e.ctx.GetStochastikVars().StmtCtx.Priority == allegrosql.NoPriority { 263 e.lowerPriority = needLowerPriority(e.plan) 264 } 265 return nil 266 } 267 268 // DeallocateInterDirc represent a DEALLOCATE interlock. 269 type DeallocateInterDirc struct { 270 baseInterlockingDirectorate 271 272 Name string 273 } 274 275 // Next implements the InterlockingDirectorate Next interface. 276 func (e *DeallocateInterDirc) Next(ctx context.Context, req *chunk.Chunk) error { 277 vars := e.ctx.GetStochastikVars() 278 id, ok := vars.PreparedStmtNameToID[e.Name] 279 if !ok { 280 return errors.Trace(causetembedded.ErrStmtNotFound) 281 } 282 preparedPointer := vars.PreparedStmts[id] 283 preparedObj, ok := preparedPointer.(*causetembedded.CachedPrepareStmt) 284 if !ok { 285 return errors.Errorf("invalid CachedPrepareStmt type") 286 } 287 prepared := preparedObj.PreparedAst 288 delete(vars.PreparedStmtNameToID, e.Name) 289 if causetembedded.PreparedCausetCacheEnabled() { 290 e.ctx.PreparedCausetCache().Delete(causetembedded.NewPSTMTCausetCacheKey( 291 vars, id, prepared.SchemaVersion, 292 )) 293 } 294 vars.RemovePreparedStmt(id) 295 return nil 296 } 297 298 // CompileInterDircutePreparedStmt compiles a stochastik InterDircute command to a stmt.Statement. 299 func CompileInterDircutePreparedStmt(ctx context.Context, sctx stochastikctx.Context, 300 ID uint32, args []types.Causet) (sqlexec.Statement, error) { 301 startTime := time.Now() 302 defer func() { 303 sctx.GetStochastikVars().DurationCompile = time.Since(startTime) 304 }() 305 execStmt := &ast.InterDircuteStmt{InterDircID: ID} 306 if err := ResetContextOfStmt(sctx, execStmt); err != nil { 307 return nil, err 308 } 309 execStmt.BinaryArgs = args 310 is := schemareplicant.GetSchemaReplicant(sctx) 311 execCauset, names, err := causet.Optimize(ctx, sctx, execStmt, is) 312 if err != nil { 313 return nil, err 314 } 315 316 stmt := &InterDircStmt{ 317 GoCtx: ctx, 318 SchemaReplicant: is, 319 Causet: execCauset, 320 StmtNode: execStmt, 321 Ctx: sctx, 322 OutputNames: names, 323 } 324 if preparedPointer, ok := sctx.GetStochastikVars().PreparedStmts[ID]; ok { 325 preparedObj, ok := preparedPointer.(*causetembedded.CachedPrepareStmt) 326 if !ok { 327 return nil, errors.Errorf("invalid CachedPrepareStmt type") 328 } 329 stmtCtx := sctx.GetStochastikVars().StmtCtx 330 stmt.Text = preparedObj.PreparedAst.Stmt.Text() 331 stmtCtx.OriginalALLEGROSQL = stmt.Text 332 stmtCtx.InitALLEGROSQLDigest(preparedObj.NormalizedALLEGROSQL, preparedObj.ALLEGROSQLDigest) 333 } 334 return stmt, nil 335 }