NewPlanCacheKey creates the plan cache key for this statement. Note: lastUpdatedSchemaVersion will only be set in the case of rc or for update read in order to differentiate the cache key. In other cases, it will be 0. All information that might affect the plan should be considered in this function.
(sctx sessionctx.Context, stmt *PlanCacheStmt)
| 255 | // differentiate the cache key. In other cases, it will be 0. |
| 256 | // All information that might affect the plan should be considered in this function. |
| 257 | func NewPlanCacheKey(sctx sessionctx.Context, stmt *PlanCacheStmt) (key, binding string, cacheable bool, reason string, err error) { |
| 258 | binding, ignored := bindinfo.MatchSQLBindingForPlanCache(sctx, stmt.PreparedAst.Stmt, &stmt.BindingInfo) |
| 259 | if ignored { |
| 260 | return "", binding, false, "ignore plan cache by binding", nil |
| 261 | } |
| 262 | |
| 263 | // In rc or for update read, we need the latest schema version to decide whether we need to |
| 264 | // rebuild the plan. So we set this value in rc or for update read. In other cases, let it be 0. |
| 265 | var latestSchemaVersion int64 |
| 266 | if sctx.GetSessionVars().IsIsolation(ast.ReadCommitted) || stmt.ForUpdateRead { |
| 267 | // In Rc or ForUpdateRead, we should check if the information schema has been changed since |
| 268 | // last time. If it changed, we should rebuild the plan. Here, we use a different and more |
| 269 | // up-to-date schema version which can lead plan cache miss and thus, the plan will be rebuilt. |
| 270 | latestSchemaVersion = domain.GetDomain(sctx).InfoSchema().SchemaMetaVersion() |
| 271 | } |
| 272 | |
| 273 | // rebuild key to exclude kv.TiFlash when stmt is not read only |
| 274 | vars := sctx.GetSessionVars() |
| 275 | if _, isolationReadContainTiFlash := vars.IsolationReadEngines[kv.TiFlash]; isolationReadContainTiFlash && !IsReadOnly(stmt.PreparedAst.Stmt, vars) { |
| 276 | delete(vars.IsolationReadEngines, kv.TiFlash) |
| 277 | defer func() { |
| 278 | vars.IsolationReadEngines[kv.TiFlash] = struct{}{} |
| 279 | }() |
| 280 | } |
| 281 | |
| 282 | if stmt.StmtText == "" { |
| 283 | return "", "", false, "", errors.New("no statement text") |
| 284 | } |
| 285 | if stmt.SchemaVersion == 0 && !intest.InTest { |
| 286 | return "", "", false, "", errors.New("Schema version uninitialized") |
| 287 | } |
| 288 | stmtDB := stmt.StmtDB |
| 289 | if stmtDB == "" { |
| 290 | stmtDB = vars.CurrentDB |
| 291 | } |
| 292 | timezoneOffset := 0 |
| 293 | if vars.TimeZone != nil { |
| 294 | _, timezoneOffset = time.Now().In(vars.TimeZone).Zone() |
| 295 | } |
| 296 | connCharset, connCollation := vars.GetCharsetInfo() |
| 297 | |
| 298 | // not allow to share the same plan among different users for safety. |
| 299 | var userName, hostName string |
| 300 | if sctx.GetSessionVars().User != nil { // might be nil if in test |
| 301 | userName = sctx.GetSessionVars().User.AuthUsername |
| 302 | hostName = sctx.GetSessionVars().User.AuthHostname |
| 303 | } |
| 304 | |
| 305 | // the user might switch the prune mode dynamically |
| 306 | pruneMode := sctx.GetSessionVars().PartitionPruneMode.Load() |
| 307 | |
| 308 | hash := make([]byte, 0, len(stmt.StmtText)*2) // TODO: a Pool for this |
| 309 | hash = append(hash, hack.Slice(userName)...) |
| 310 | hash = append(hash, hack.Slice(hostName)...) |
| 311 | hash = append(hash, hack.Slice(stmtDB)...) |
| 312 | hash = append(hash, hack.Slice(stmt.StmtText)...) |
| 313 | hash = codec.EncodeInt(hash, stmt.SchemaVersion) |
| 314 | hash = hashInt64Uint64Map(hash, stmt.RelateVersion) |
no test coverage detected