Skip to content
Snippets Groups Projects
Unverified Commit 95c654e0 authored by ou yuanning's avatar ou yuanning Committed by GitHub
Browse files

Build tcl plan (#2502)

* build TCL plan

* minor improve

* improve test code
parent 65b0952f
No related branches found
No related tags found
No related merge requests found
......@@ -952,7 +952,7 @@ func (mce *MysqlCmdExecutor) handleExplainStmt(stmt *tree.ExplainStmt) error {
// build explain data buffer
buffer := explain.NewExplainDataBuffer()
// generator query explain
explainQuery := explain.NewExplainQueryImpl(qry)
explainQuery := explain.NewExplainQueryImpl(qry.GetQuery())
explainQuery.ExplainPlan(buffer, es)
session := mce.GetSession()
......
This diff is collapsed.
......@@ -25,11 +25,11 @@ const (
//modes for a transaction
type TransactionModes struct {
rwMode ReadWriteMode
RwMode ReadWriteMode
}
func (node *TransactionModes) Format(ctx *FmtCtx) {
switch node.rwMode {
switch node.RwMode {
case READ_WRITE_MODE_READ_ONLY:
ctx.WriteString("read only")
case READ_WRITE_MODE_READ_WRITE:
......@@ -38,7 +38,7 @@ func (node *TransactionModes) Format(ctx *FmtCtx) {
}
func MakeTransactionModes(rwm ReadWriteMode) TransactionModes {
return TransactionModes{rwMode: rwm}
return TransactionModes{RwMode: rwm}
}
//Begin statement
......@@ -49,7 +49,7 @@ type BeginTransaction struct {
func (node *BeginTransaction) Format(ctx *FmtCtx) {
ctx.WriteString("start transaction")
if node.Modes.rwMode != READ_WRITE_MODE_NONE {
if node.Modes.RwMode != READ_WRITE_MODE_NONE {
ctx.WriteByte(' ')
node.Modes.Format(ctx)
}
......
......@@ -24,36 +24,35 @@ import (
"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
)
func BuildPlan(ctx CompilerContext, stmt tree.Statement) (*plan.Query, error) {
query := &Query{}
err := buildStatement(stmt, ctx, query)
if err != nil {
return nil, err
func BuildPlan(ctx CompilerContext, stmt tree.Statement) (*plan.Plan, error) {
runBuildSelect := func(stmt *tree.Select) (*plan.Plan, error) {
query, selectCtx := newQueryAndSelectCtx(plan.Query_SELECT)
err := buildSelect(stmt, ctx, query, selectCtx)
return &plan.Plan{
Plan: &plan.Plan_Query{
Query: query,
},
}, err
}
return (*plan.Query)(query), nil
}
func buildStatement(stmt tree.Statement, ctx CompilerContext, query *Query) error {
selectCtx := &SelectContext{
columnAlias: make(map[string]*plan.Expr),
cteTables: make(map[string]*plan.TableDef),
}
switch stmt := stmt.(type) {
case *tree.Select:
query.StmtType = plan.Query_SELECT
return buildSelect(stmt, ctx, query, selectCtx)
return runBuildSelect(stmt)
case *tree.ParenSelect:
query.StmtType = plan.Query_SELECT
return buildSelect(stmt.Select, ctx, query, selectCtx)
return runBuildSelect(stmt.Select)
case *tree.Insert:
query.StmtType = plan.Query_INSERT
return buildInsert(stmt, ctx, query)
return buildInsert(stmt, ctx)
case *tree.Update:
query.StmtType = plan.Query_UPDATE
return buildUpdate(stmt, ctx, query)
return buildUpdate(stmt, ctx)
case *tree.Delete:
query.StmtType = plan.Query_DELETE
return buildDelete(stmt, ctx, query)
return buildDelete(stmt, ctx)
case *tree.BeginTransaction:
return buildBeginTransaction(stmt, ctx)
case *tree.CommitTransaction:
return buildCommitTransaction(stmt, ctx)
case *tree.RollbackTransaction:
return buildRollbackTransaction(stmt, ctx)
default:
return nil, errors.New(errno.SQLStatementNotYetComplete, fmt.Sprintf("unexpected statement: '%v'", tree.String(stmt, dialect.MYSQL)))
}
return errors.New(errno.SQLStatementNotYetComplete, fmt.Sprintf("unexpected statement: '%v'", tree.String(stmt, dialect.MYSQL)))
}
......@@ -23,7 +23,7 @@ import (
"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
)
func buildDelete(stmt *tree.Delete, ctx CompilerContext, query *Query) error {
func buildDelete(stmt *tree.Delete, ctx CompilerContext) (*plan.Plan, error) {
selectStmt := &tree.Select{
Select: &tree.SelectClause{
//todo confirm how to set row_id
......@@ -38,22 +38,20 @@ func buildDelete(stmt *tree.Delete, ctx CompilerContext, query *Query) error {
OrderBy: stmt.OrderBy,
Limit: stmt.Limit,
}
selectCtx := &SelectContext{
columnAlias: make(map[string]*plan.Expr),
}
query, selectCtx := newQueryAndSelectCtx(plan.Query_DELETE)
err := buildSelect(selectStmt, ctx, query, selectCtx)
if err != nil {
return err
return nil, err
}
return appendDeleteNode(query)
}
func appendDeleteNode(query *Query) error {
func appendDeleteNode(query *Query) (*plan.Plan, error) {
//get tableDef
objRef, tableDef := getLastTableDef(query)
if tableDef == nil {
return errors.New(errno.SyntaxErrororAccessRuleViolation, fmt.Sprintf("cannot find delete table"))
return nil, errors.New(errno.SyntaxErrororAccessRuleViolation, fmt.Sprintf("cannot find delete table"))
}
//append delete node
......@@ -68,5 +66,9 @@ func appendDeleteNode(query *Query) error {
preNode := query.Nodes[len(query.Nodes)-1]
query.Steps[len(query.Steps)-1] = preNode.NodeId
return nil
return &plan.Plan{
Plan: &plan.Plan_Query{
Query: query,
},
}, nil
}
......@@ -25,11 +25,13 @@ import (
"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
)
func buildInsert(stmt *tree.Insert, ctx CompilerContext, query *Query) error {
func buildInsert(stmt *tree.Insert, ctx CompilerContext) (*plan.Plan, error) {
query, _ := newQueryAndSelectCtx(plan.Query_INSERT)
//get table
objRef, tableDef, err := getInsertTable(stmt.Table, ctx, query)
if err != nil {
return err
return nil, err
}
//get columns
......@@ -49,7 +51,7 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, query *Query) error {
columnName := string(identifier)
col, err := getColDef(columnName)
if err != nil {
return err
return nil, err
}
columns = append(columns, col)
}
......@@ -67,12 +69,11 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, query *Query) error {
switch rows := stmt.Rows.Select.(type) {
case *tree.Select:
selectCtx := &SelectContext{
// tableAlias: make(map[string]string),
columnAlias: make(map[string]*plan.Expr),
}
err := buildSelect(rows, ctx, query, selectCtx)
if err != nil {
return err
return nil, err
}
//fixme need check preNode's projectionList match rowset.Schema
case *tree.ValuesClause:
......@@ -84,7 +85,7 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, query *Query) error {
}
err := getValues(rowset, rows, columnCount)
if err != nil {
return err
return nil, err
}
node := &plan.Node{
NodeType: plan.Node_VALUE_SCAN,
......@@ -92,7 +93,7 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, query *Query) error {
}
appendQueryNode(query, node, false)
default:
return errors.New(errno.SQLStatementNotYetComplete, fmt.Sprintf("unsupport rows expr: %T", stmt))
return nil, errors.New(errno.SQLStatementNotYetComplete, fmt.Sprintf("unsupport rows expr: %T", stmt))
}
node := &plan.Node{
......@@ -103,9 +104,17 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, query *Query) error {
appendQueryNode(query, node, false)
preNode := query.Nodes[len(query.Nodes)-1]
query.Steps = append(query.Steps, preNode.NodeId)
if len(query.Steps) > 0 {
query.Steps[len(query.Steps)-1] = preNode.NodeId
} else {
query.Steps = append(query.Steps, preNode.NodeId)
}
return nil
return &plan.Plan{
Plan: &plan.Plan_Query{
Query: query,
},
}, nil
}
func getValues(rowset *plan.RowsetData, rows *tree.ValuesClause, columnCount int) error {
......
......@@ -117,11 +117,20 @@ func buildCTE(withExpr *tree.With, ctx CompilerContext, query *Query, selectCtx
return nil
}
var err error
for _, cte := range withExpr.CTEs {
err := buildStatement(cte.Stmt, ctx, query)
switch stmt := cte.Stmt.(type) {
case *tree.Select:
err = buildSelect(stmt, ctx, query, selectCtx)
case *tree.ParenSelect:
err = buildSelect(stmt.Select, ctx, query, selectCtx)
default:
err = errors.New(errno.SQLStatementNotYetComplete, fmt.Sprintf("unexpected statement: '%v'", tree.String(stmt, dialect.MYSQL)))
}
if err != nil {
return err
}
//add a projection node
alias := string(cte.Name.Alias)
node := &plan.Node{
......
......@@ -27,16 +27,16 @@ import (
//only use in developing
func TestSingleSql(t *testing.T) {
sql := `DELETE FROM NATION WHERE N_NATIONKEY > 10 LIMIT 20`
sql := `commit and no chain`
// stmts, _ := mysql.Parse(sql)
// t.Logf("%+v", string(getJson(stmts[0], t)))
mock := NewMockOptimizer()
query, err := runOneStmt(mock, t, sql)
logicPlan, err := runOneStmt(mock, t, sql)
if err != nil {
t.Fatalf("%+v", err)
}
outPutQuery(query, true, t)
outPutPlan(logicPlan, true, t)
}
//Test Query Node Tree
......@@ -294,7 +294,8 @@ func TestNodeTree(t *testing.T) {
//run test and check node tree
for sql, check := range nodeTreeCheckList {
mock := NewMockOptimizer()
query, err := runOneStmt(mock, t, sql)
logicPlan, err := runOneStmt(mock, t, sql)
query := logicPlan.GetQuery()
if err != nil {
t.Fatalf("%+v, sql=%v", err, sql)
}
......@@ -473,7 +474,7 @@ func TestDelete(t *testing.T) {
// should error
sqls = []string{
"DELETE FROM NATION2222", // table not exist
"DELETE FROM NATION WHERE N_NATIONKEY2 > 10", // column type not match
"DELETE FROM NATION WHERE N_NATIONKEY2 > 10", // column not found
}
runTestShouldError(mock, t, sqls)
......@@ -515,6 +516,25 @@ func TestSubQuery(t *testing.T) {
}
func TestTcl(t *testing.T) {
mock := NewMockOptimizer()
//should pass
sqls := []string{
"start transaction",
"start transaction read write",
"begin",
"commit and chain",
"commit and chain no release",
"rollback and chain",
}
runTestShouldPass(mock, t, sqls, false, false)
// should error
sqls = []string{}
runTestShouldError(mock, t, sqls)
}
func getJson(v any, t *testing.T) []byte {
b, err := json.Marshal(v)
if err != nil {
......@@ -528,8 +548,14 @@ func getJson(v any, t *testing.T) []byte {
return out.Bytes()
}
func outPutQuery(query *plan.Query, toFile bool, t *testing.T) {
json := getJson(query, t)
func outPutPlan(logicPlan *plan.Plan, toFile bool, t *testing.T) {
var json []byte
switch logicPlan.Plan.(type) {
case *plan.Plan_Query:
json = getJson(logicPlan.GetQuery(), t)
case *plan.Plan_Tcl:
json = getJson(logicPlan.GetTcl(), t)
}
if toFile {
err := ioutil.WriteFile("/tmp/mo_plan2_test.json", json, 0777)
if err != nil {
......@@ -540,23 +566,24 @@ func outPutQuery(query *plan.Query, toFile bool, t *testing.T) {
}
}
func runOneStmt(opt Optimizer, t *testing.T, sql string) (*plan.Query, error) {
func runOneStmt(opt Optimizer, t *testing.T, sql string) (*plan.Plan, error) {
stmts, err := mysql.Parse(sql)
if err != nil {
t.Fatalf("%+v", err)
}
//this sql always return one stmt
return opt.Optimize(stmts[0])
ctx := opt.CurrentContext()
return BuildPlan(ctx, stmts[0])
}
func runTestShouldPass(opt Optimizer, t *testing.T, sqls []string, printJson bool, toFile bool) {
for _, sql := range sqls {
query, err := runOneStmt(opt, t, sql)
logicPlan, err := runOneStmt(opt, t, sql)
if err != nil {
t.Fatalf("%+v", err)
}
if printJson {
outPutQuery(query, toFile, t)
outPutPlan(logicPlan, toFile, t)
}
}
}
......
// Copyright 2021 - 2022 Matrix Origin
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package plan2
import (
"github.com/matrixorigin/matrixone/pkg/pb/plan"
"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
)
func buildBeginTransaction(stmt *tree.BeginTransaction, ctx CompilerContext) (*plan.Plan, error) {
beginTransation := &plan.TransationBegin{}
switch stmt.Modes.RwMode {
case tree.READ_WRITE_MODE_NONE:
beginTransation.Mode = plan.TransationBegin_NONE
case tree.READ_WRITE_MODE_READ_ONLY:
beginTransation.Mode = plan.TransationBegin_READ_ONLY
case tree.READ_WRITE_MODE_READ_WRITE:
beginTransation.Mode = plan.TransationBegin_READ_WRITE
}
return &plan.Plan{
Plan: &plan.Plan_Tcl{
Tcl: &plan.TransationControl{
TclType: plan.TransationControl_BEGIN,
Action: &plan.TransationControl_Begin{
Begin: beginTransation,
},
},
},
}, nil
}
func buildCommitTransaction(stmt *tree.CommitTransaction, ctx CompilerContext) (*plan.Plan, error) {
commitTransation := &plan.TransationCommit{}
switch stmt.Type {
case tree.COMPLETION_TYPE_CHAIN:
commitTransation.CompletionType = plan.TransationCompletionType_CHAIN
case tree.COMPLETION_TYPE_RELEASE:
commitTransation.CompletionType = plan.TransationCompletionType_RELEASE
case tree.COMPLETION_TYPE_NO_CHAIN:
commitTransation.CompletionType = plan.TransationCompletionType_NO_CHAIN
}
return &plan.Plan{
Plan: &plan.Plan_Tcl{
Tcl: &plan.TransationControl{
TclType: plan.TransationControl_COMMIT,
Action: &plan.TransationControl_Commit{
Commit: commitTransation,
},
},
},
}, nil
}
func buildRollbackTransaction(stmt *tree.RollbackTransaction, ctx CompilerContext) (*plan.Plan, error) {
rollbackTransation := &plan.TransationRollback{}
switch stmt.Type {
case tree.COMPLETION_TYPE_CHAIN:
rollbackTransation.CompletionType = plan.TransationCompletionType_CHAIN
case tree.COMPLETION_TYPE_RELEASE:
rollbackTransation.CompletionType = plan.TransationCompletionType_RELEASE
case tree.COMPLETION_TYPE_NO_CHAIN:
rollbackTransation.CompletionType = plan.TransationCompletionType_NO_CHAIN
}
return &plan.Plan{
Plan: &plan.Plan_Tcl{
Tcl: &plan.TransationControl{
TclType: plan.TransationControl_ROLLBACK,
Action: &plan.TransationControl_Rollback{
Rollback: rollbackTransation,
},
},
},
}, nil
}
......@@ -23,7 +23,9 @@ import (
"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
)
func buildUpdate(stmt *tree.Update, ctx CompilerContext, query *Query) error {
func buildUpdate(stmt *tree.Update, ctx CompilerContext) (*plan.Plan, error) {
query, _ := newQueryAndSelectCtx(plan.Query_UPDATE)
//build select
selectStmt := &tree.Select{
Select: &tree.SelectClause{
......@@ -39,18 +41,17 @@ func buildUpdate(stmt *tree.Update, ctx CompilerContext, query *Query) error {
Limit: stmt.Limit,
}
selectCtx := &SelectContext{
// tableAlias: make(map[string]string),
columnAlias: make(map[string]*plan.Expr),
}
err := buildSelect(selectStmt, ctx, query, selectCtx)
if err != nil {
return err
return nil, err
}
//get table def
objRef, tableDef := getLastTableDef(query)
if tableDef == nil {
return errors.New(errno.CaseNotFound, "can not find table in sql")
return nil, errors.New(errno.CaseNotFound, "can not find table in sql")
}
getColumnName := func(name string) *plan.Expr {
......@@ -73,34 +74,34 @@ func buildUpdate(stmt *tree.Update, ctx CompilerContext, query *Query) error {
columnLength := len(stmt.Exprs)
if columnLength == 0 {
return errors.New(errno.CaseNotFound, "no column will be update")
return nil, errors.New(errno.CaseNotFound, "no column will be update")
}
columns := make([]*plan.Expr, 0, columnLength)
values := make([]*plan.Expr, 0, columnLength)
for _, expr := range stmt.Exprs {
if len(expr.Names) != 1 {
return errors.New(errno.CaseNotFound, "the set list of update must be one")
return nil, errors.New(errno.CaseNotFound, "the set list of update must be one")
}
if expr.Names[0].NumParts != 1 {
return errors.New(errno.CaseNotFound, "the set list of update must be one")
return nil, errors.New(errno.CaseNotFound, "the set list of update must be one")
}
column := getColumnName(expr.Names[0].Parts[0])
if column == nil {
return errors.New(errno.CaseNotFound, fmt.Sprintf("set column name [%v] is not found", expr.Names[0].Parts[0]))
return nil, errors.New(errno.CaseNotFound, fmt.Sprintf("set column name [%v] is not found", expr.Names[0].Parts[0]))
}
value, err := buildExpr(expr.Expr, ctx, query, selectCtx)
if err != nil {
return err
return nil, err
}
//cast value type
if column.Typ.Id != value.Typ.Id {
tmp, err := appendCastExpr(value, column.Typ.Id)
if err != nil {
return err
return nil, err
}
value = tmp
}
......@@ -121,7 +122,11 @@ func buildUpdate(stmt *tree.Update, ctx CompilerContext, query *Query) error {
appendQueryNode(query, node, false)
preNode := query.Nodes[len(query.Nodes)-1]
query.Steps = append(query.Steps, preNode.NodeId)
query.Steps[len(query.Steps)-1] = preNode.NodeId
return nil
return &plan.Plan{
Plan: &plan.Plan_Query{
Query: query,
},
}, nil
}
......@@ -373,3 +373,14 @@ func getLastTableDef(query *Query) (*plan.ObjectRef, *plan.TableDef) {
}
return nil, nil
}
func newQueryAndSelectCtx(typ plan.Query_StatementType) (*Query, *SelectContext) {
selectCtx := &SelectContext{
columnAlias: make(map[string]*plan.Expr),
cteTables: make(map[string]*plan.TableDef),
}
query := &Query{
StmtType: typ,
}
return query, selectCtx
}
......@@ -188,14 +188,13 @@ func runOneStmt(opt plan2.Optimizer, t *testing.T, sql string) error {
//this sql always return one stmt
ctx := opt.CurrentContext()
query, err := plan2.BuildPlan(ctx, stmt.Statement)
logicPlan, err := plan2.BuildPlan(ctx, stmt.Statement)
if err != nil {
fmt.Printf("Build Query Plan error: '%v'", tree.String(stmt, dialect.MYSQL))
return err
}
buffer := NewExplainDataBuffer()
explainQuery := NewExplainQueryImpl(query)
explainQuery := NewExplainQueryImpl(logicPlan.GetQuery())
explainQuery.ExplainPlan(buffer, es)
// t.Logf("\n")
}
......
......@@ -197,14 +197,14 @@ func NewMockOptimizer() *MockOptimizer {
}
}
func (moc *MockOptimizer) Optimize(stmt tree.Statement) (*plan.Query, error) {
func (moc *MockOptimizer) Optimize(stmt tree.Statement) (*Query, error) {
ctx := moc.CurrentContext()
query, err := BuildPlan(ctx, stmt)
if err != nil {
// fmt.Printf("Optimize statement error: '%v'", tree.String(stmt, dialect.MYSQL))
return nil, err
}
return query, nil
return query.GetQuery(), nil
}
func (moc *MockOptimizer) CurrentContext() CompilerContext {
......
......@@ -30,11 +30,11 @@ type Query = plan.Query
type CompilerContext interface {
Resolve(name string) (*ObjectRef, *TableDef)
Cost(obj *ObjectRef, e *Expr) *Cost //change Cost to *Cost to fixed "return copies lock value" warning in new proto code generated
Cost(obj *ObjectRef, e *Expr) *Cost
}
type Optimizer interface {
Optimize(stmt tree.Statement) (*Query, error) //todo confirm interface change
Optimize(stmt tree.Statement) (*Query, error)
CurrentContext() CompilerContext
}
......
......@@ -323,15 +323,6 @@ message Node {
string extra_options = 19;
}
enum StatementType {
UNKNOW = 0;
SELECT = 1;
INSERT = 2;
DELETE = 3;
UPDATE = 4;
MERGE = 5;
}
message Query {
enum StatementType {
UNKNOWN = 0;
......@@ -362,13 +353,48 @@ message Query {
repeated Expr params = 4;
}
message TransationControl {
enum TclType {
BEGIN = 0;
COMMIT = 1;
ROLLBACK = 2;
}
//TransationControl type
TclType tcl_type = 1;
oneof action {
TransationBegin begin = 2;
TransationCommit commit = 3;
TransationRollback rollback = 4;
}
}
message TransationBegin {
enum TransationMode {
NONE = 0;
READ_ONLY = 1;
READ_WRITE = 2;
}
TransationMode mode = 1;
}
enum TransationCompletionType {
CHAIN = 0;
NO_CHAIN = 1;
RELEASE = 2;
}
message TransationCommit {
TransationCompletionType completion_type = 1;
}
message TransationRollback {
TransationCompletionType completion_type = 1;
}
message Plan {
oneof plan {
Query query = 1;
TransationControl tcl =2;
}
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment