// Copyright 2016 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. package executor import ( "strconv" "strings" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/optimizer/plan" "github.com/pingcap/tidb/parser/opcode" "github.com/pingcap/tidb/util/types" ) type explainEntry struct { ID int64 selectType string table string joinType string possibleKeys string key string keyLen string ref string rows int64 extra []string } func (e *explainEntry) setJoinTypeForTableScan(p *plan.TableScan) { if len(p.AccessConditions) == 0 { e.joinType = "ALL" return } if p.RefAccess { e.joinType = "eq_ref" return } for _, con := range p.AccessConditions { if x, ok := con.(*ast.BinaryOperationExpr); ok { if x.Op == opcode.EQ { e.joinType = "const" return } } } e.joinType = "range" } func (e *explainEntry) setJoinTypeForIndexScan(p *plan.IndexScan) { if len(p.AccessConditions) == 0 { e.joinType = "index" return } if len(p.AccessConditions) == p.AccessEqualCount { if p.RefAccess { if p.Index.Unique { e.joinType = "eq_ref" } else { e.joinType = "ref" } } else { if p.Index.Unique { e.joinType = "const" } else { e.joinType = "range" } } return } e.joinType = "range" } // ExplainExec represents an explain executor. // See: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html type ExplainExec struct { StmtPlan plan.Plan fields []*ast.ResultField rows []*Row cursor int } // Fields implements Executor Fields interface. func (e *ExplainExec) Fields() []*ast.ResultField { return e.fields } // Next implements Execution Next interface. func (e *ExplainExec) Next() (*Row, error) { if e.rows == nil { e.fetchRows() } if e.cursor >= len(e.rows) { return nil, nil } row := e.rows[e.cursor] e.cursor++ return row, nil } func (e *ExplainExec) fetchRows() { visitor := &explainVisitor{id: 1} e.StmtPlan.Accept(visitor) for _, entry := range visitor.entries { row := &Row{} row.Data = types.MakeDatums( entry.ID, entry.selectType, entry.table, entry.joinType, entry.key, entry.key, entry.keyLen, entry.ref, entry.rows, strings.Join(entry.extra, "; "), ) for i := range row.Data { if row.Data[i].Kind() == types.KindString && row.Data[i].GetString() == "" { row.Data[i].SetNull() } } e.rows = append(e.rows, row) } } // Close implements Executor Close interface. func (e *ExplainExec) Close() error { return nil } type explainVisitor struct { id int64 // Sort extra should be appended in the first table in a join. sort bool entries []*explainEntry } func (v *explainVisitor) Enter(p plan.Plan) (plan.Plan, bool) { switch x := p.(type) { case *plan.TableScan: v.entries = append(v.entries, v.newEntryForTableScan(x)) case *plan.IndexScan: v.entries = append(v.entries, v.newEntryForIndexScan(x)) case *plan.Sort: v.sort = true } return p, false } func (v *explainVisitor) Leave(p plan.Plan) (plan.Plan, bool) { return p, true } func (v *explainVisitor) newEntryForTableScan(p *plan.TableScan) *explainEntry { entry := &explainEntry{ ID: v.id, selectType: "SIMPLE", table: p.Table.Name.O, } entry.setJoinTypeForTableScan(p) if entry.joinType != "ALL" { entry.key = "PRIMARY" entry.keyLen = "8" } if len(p.AccessConditions)+len(p.FilterConditions) > 0 { entry.extra = append(entry.extra, "Using where") } v.setSortExtra(entry) return entry } func (v *explainVisitor) newEntryForIndexScan(p *plan.IndexScan) *explainEntry { entry := &explainEntry{ ID: v.id, selectType: "SIMPLE", table: p.Table.Name.O, key: p.Index.Name.O, } if len(p.AccessConditions) != 0 { keyLen := 0 for i := 0; i < len(p.Index.Columns); i++ { if i < p.AccessEqualCount { keyLen += p.Index.Columns[i].Length } else if i < len(p.AccessConditions) { keyLen += p.Index.Columns[i].Length break } } entry.keyLen = strconv.Itoa(keyLen) } entry.setJoinTypeForIndexScan(p) if len(p.AccessConditions)+len(p.FilterConditions) > 0 { entry.extra = append(entry.extra, "Using where") } v.setSortExtra(entry) return entry } func (v *explainVisitor) setSortExtra(entry *explainEntry) { if v.sort { entry.extra = append(entry.extra, "Using filesort") v.sort = false } } nge/debt/core_main.js'>artonge/debt/core_main.js Nextcloud server, a safe home for all your data: https://github.com/nextcloud/serverwww-data
aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public/WorkflowEngine/IRuleMatcher.php
blob: 06a5d1680b0ea48ae11925a0db315161d14c8044 (plain)
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
<?php

declare(strict_types=1);

/**
 * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de>
 *
 * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

namespace OCP\WorkflowEngine;

use RuntimeException;

/**
 * Class IRuleMatcher
 *
 *
 * @since 18.0.0
 */
interface IRuleMatcher extends IFileCheck {
	/**
	 * This method is left for backwards compatibility and easier porting of
	 * apps. Please use 'getFlows' instead (and setOperation if you implement
	 * an IComplexOperation).
	 *
	 * @since 18.0.0
	 * @deprecated 18.0.0
	 */
	public function getMatchingOperations(string $class, bool $returnFirstMatchingOperationOnly = true): array;

	/**
	 * @throws RuntimeException
	 * @since 18.0.0
	 */
	public function getFlows(bool $returnFirstMatchingOperationOnly = true): array;

	/**
	 * this method can only be called once and is typically called by the
	 * Flow engine, unless for IComplexOperations.
	 *
	 * @throws RuntimeException
	 * @since 18.0.0
	 */
	public function setOperation(IOperation $operation): void;

	/**
	 * this method can only be called once and is typically called by the
	 * Flow engine, unless for IComplexOperations.
	 *
	 * @throws RuntimeException
	 * @since 18.0.0
	 */
	public function setEntity(IEntity $entity): void;

	/**
	 * returns the entity which might provide more information, depending on
	 * the interfaces it implements
	 *
	 * @return IEntity
	 * @since 18.0.0
	 */
	public function getEntity(): IEntity;

	/**
	 * this method can be called once to set the event name that is currently
	 * being processed. The workflow engine takes care of this usually, only an
	 * IComplexOperation might want to make use of it.
	 *
	 * @throws RuntimeException
	 * @since 20.0.0
	 */
	public function setEventName(string $eventName): void;
}