github.com/vescale/zgraph@v0.0.0-20230410094002-959c02d50f95/planner/plan_builder.go (about)

     1  // Copyright 2022 zGraph Authors. All rights reserved.
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package planner
    16  
    17  import (
    18  	"bytes"
    19  
    20  	"github.com/pingcap/errors"
    21  	"github.com/vescale/zgraph/catalog"
    22  	"github.com/vescale/zgraph/expression"
    23  	"github.com/vescale/zgraph/meta"
    24  	"github.com/vescale/zgraph/parser/ast"
    25  	"github.com/vescale/zgraph/parser/format"
    26  	"github.com/vescale/zgraph/parser/model"
    27  	"github.com/vescale/zgraph/stmtctx"
    28  )
    29  
    30  // builderContext represents the context of building plan.
    31  type builderContext struct {
    32  	plan Plan
    33  }
    34  
    35  // Builder is used to build the AST into a plan.
    36  type Builder struct {
    37  	sc     *stmtctx.Context
    38  	stacks []*builderContext
    39  }
    40  
    41  // NewBuilder returns a plan builder.
    42  func NewBuilder(sc *stmtctx.Context) *Builder {
    43  	return &Builder{
    44  		sc: sc,
    45  	}
    46  }
    47  
    48  // Build builds a statement AST node into a Plan.
    49  func (b *Builder) Build(node ast.StmtNode) (Plan, error) {
    50  	b.pushContext()
    51  	defer b.popContext()
    52  
    53  	var err error
    54  	switch stmt := node.(type) {
    55  	case ast.DDLNode:
    56  		err = b.buildDDL(stmt)
    57  	case *ast.UseStmt:
    58  		err = b.buildSimple(stmt)
    59  	case *ast.InsertStmt:
    60  		err = b.buildInsert(stmt)
    61  	case *ast.SelectStmt:
    62  		err = b.buildSelect(stmt)
    63  	case *ast.ShowStmt:
    64  		err = b.buildShow(stmt)
    65  	}
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return b.plan(), nil
    71  }
    72  
    73  func (b *Builder) pushContext() {
    74  	b.stacks = append(b.stacks, &builderContext{})
    75  }
    76  
    77  func (b *Builder) popContext() {
    78  	b.stacks = b.stacks[:len(b.stacks)-1]
    79  }
    80  
    81  func (b *Builder) plan() Plan {
    82  	return b.stacks[len(b.stacks)-1].plan
    83  }
    84  
    85  func (b *Builder) setPlan(plan Plan) {
    86  	b.stacks[len(b.stacks)-1].plan = plan
    87  }
    88  
    89  func (b *Builder) buildDDL(ddl ast.DDLNode) error {
    90  	b.setPlan(&DDL{
    91  		Statement: ddl,
    92  	})
    93  	return nil
    94  }
    95  
    96  func (b *Builder) buildSimple(stmt ast.StmtNode) error {
    97  	b.setPlan(&Simple{
    98  		Statement: stmt,
    99  	})
   100  	return nil
   101  }
   102  
   103  func (b *Builder) buildInsert(stmt *ast.InsertStmt) error {
   104  	var intoGraph string
   105  	if !stmt.IntoGraphName.IsEmpty() {
   106  		intoGraph = stmt.IntoGraphName.L
   107  	}
   108  	var graph *catalog.Graph
   109  	if intoGraph == "" {
   110  		graph = b.sc.CurrentGraph()
   111  	} else {
   112  		graph = b.sc.Catalog().Graph(intoGraph)
   113  	}
   114  	if graph == nil {
   115  		return errors.Annotatef(meta.ErrGraphNotExists, "graph %s", intoGraph)
   116  	}
   117  
   118  	var fromPlan LogicalPlan
   119  	if stmt.From != nil {
   120  		p, err := b.buildMatch(stmt.From.Matches)
   121  		if err != nil {
   122  			return err
   123  		}
   124  
   125  		if stmt.Where != nil {
   126  			cond, err := RewriteExpr(stmt.Where, p)
   127  			if err != nil {
   128  				return err
   129  			}
   130  			where := &LogicalSelection{
   131  				Condition: cond,
   132  			}
   133  			where.SetChildren(p)
   134  			p = where
   135  		}
   136  		fromPlan = p
   137  
   138  	} else {
   139  		fromPlan = &LogicalDual{}
   140  	}
   141  
   142  	var insertions []*ElementInsertion
   143  	for _, insertion := range stmt.Insertions {
   144  		var labels []*catalog.Label
   145  		for _, lbl := range insertion.LabelsAndProperties.Labels {
   146  			label := graph.Label(lbl.L)
   147  			if label == nil {
   148  				return errors.Annotatef(meta.ErrLabelNotExists, "label %s", lbl.L)
   149  			}
   150  			labels = append(labels, label)
   151  		}
   152  		var assignments []*expression.Assignment
   153  		for _, prop := range insertion.LabelsAndProperties.Assignments {
   154  			// Note: The property suppose to be exists because we have invoked property
   155  			// creation module before building plan.
   156  			propInfo := graph.Property(prop.PropertyAccess.PropertyName.L)
   157  			// Please fix bug in PropertyPreparation if the propInfo empty.
   158  			if propInfo == nil {
   159  				return errors.Errorf("property %s not exists", prop.PropertyAccess.PropertyName.L)
   160  			}
   161  			expr, err := RewriteExpr(prop.ValueExpression, fromPlan)
   162  			if err != nil {
   163  				return err
   164  			}
   165  			assignment := &expression.Assignment{
   166  				VariableRef: &expression.VariableRef{Name: prop.PropertyAccess.VariableName},
   167  				PropertyRef: &expression.PropertyRef{Property: propInfo},
   168  				Expr:        expr,
   169  			}
   170  			assignments = append(assignments, assignment)
   171  		}
   172  		var fromIDExpr, toIDExpr expression.Expression
   173  		if insertion.InsertionType == ast.InsertionTypeEdge {
   174  			fromExpr, err := RewriteExpr(&ast.VariableReference{VariableName: insertion.From}, fromPlan)
   175  			if err != nil {
   176  				return err
   177  			}
   178  			fromIDExpr, err = expression.NewFuncExpr("id", fromExpr)
   179  			if err != nil {
   180  				return err
   181  			}
   182  			toExpr, err := RewriteExpr(&ast.VariableReference{VariableName: insertion.To}, fromPlan)
   183  			if err != nil {
   184  				return err
   185  			}
   186  			toIDExpr, err = expression.NewFuncExpr("id", toExpr)
   187  			if err != nil {
   188  				return err
   189  			}
   190  		}
   191  		gi := &ElementInsertion{
   192  			Type:        insertion.InsertionType,
   193  			Labels:      labels,
   194  			Assignments: assignments,
   195  			FromIDExpr:  fromIDExpr,
   196  			ToIDExpr:    toIDExpr,
   197  		}
   198  		insertions = append(insertions, gi)
   199  	}
   200  
   201  	plan := &Insert{
   202  		Graph:      graph,
   203  		Insertions: insertions,
   204  	}
   205  	if stmt.From != nil {
   206  		plan.MatchPlan = Optimize(fromPlan)
   207  	}
   208  
   209  	b.setPlan(plan)
   210  	return nil
   211  }
   212  
   213  func (b *Builder) buildSelect(stmt *ast.SelectStmt) error {
   214  	// Build source
   215  	plan, err := b.buildMatch(stmt.From.Matches)
   216  	if err != nil {
   217  		return err
   218  	}
   219  
   220  	// Build selection
   221  	if stmt.Where != nil {
   222  		expr, err := RewriteExpr(stmt.Where, plan)
   223  		if err != nil {
   224  			return err
   225  		}
   226  		where := &LogicalSelection{
   227  			Condition: expr,
   228  		}
   229  		where.SetChildren(plan)
   230  		plan = where
   231  	}
   232  
   233  	// TODO: support GROUP BY clause.
   234  	// Explicit GROUP BY: SELECT * FROM MATCH (n) GROUP BY n.name;
   235  	// Implicit GROUP BY: SELECT COUNT(*) FROM MATCH (n);
   236  	if stmt.GroupBy != nil {
   237  
   238  	}
   239  
   240  	if stmt.Having != nil {
   241  		expr, err := RewriteExpr(stmt.Having.Expr, plan)
   242  		if err != nil {
   243  			return err
   244  		}
   245  		having := &LogicalSelection{
   246  			Condition: expr,
   247  		}
   248  		having.SetChildren(plan)
   249  		plan = having
   250  	}
   251  
   252  	if stmt.OrderBy != nil {
   253  		byItems := make([]*ByItem, 0, len(stmt.OrderBy.Items))
   254  		for _, item := range stmt.OrderBy.Items {
   255  			expr, err := RewriteExpr(item.Expr.Expr, plan)
   256  			if err != nil {
   257  				return err
   258  			}
   259  			byItems = append(byItems, &ByItem{
   260  				Expr:      expr,
   261  				AsName:    item.Expr.AsName,
   262  				Desc:      item.Desc,
   263  				NullOrder: item.NullOrder,
   264  			})
   265  		}
   266  		orderby := &LogicalSort{
   267  			ByItems: byItems,
   268  		}
   269  		orderby.SetChildren(plan)
   270  		plan = orderby
   271  	}
   272  
   273  	if stmt.Limit != nil {
   274  		offset, err := RewriteExpr(stmt.Limit.Offset, plan)
   275  		if err != nil {
   276  			return err
   277  		}
   278  		count, err := RewriteExpr(stmt.Limit.Count, plan)
   279  		if err != nil {
   280  			return err
   281  		}
   282  		limit := &LogicalLimit{
   283  			Offset: offset,
   284  			Count:  count,
   285  		}
   286  		limit.SetChildren(plan)
   287  		plan = limit
   288  	}
   289  
   290  	cols := make(ResultColumns, 0, len(stmt.Select.Elements))
   291  	// TODO: support DISTINCT and wildcard.
   292  	proj := &LogicalProjection{}
   293  	for _, elem := range stmt.Select.Elements {
   294  		expr, err := RewriteExpr(elem.ExpAsVar.Expr, plan)
   295  		if err != nil {
   296  			return err
   297  		}
   298  		proj.Exprs = append(proj.Exprs, expr)
   299  
   300  		var colName model.CIStr
   301  		if elem.ExpAsVar.AsName.IsEmpty() {
   302  			var buf bytes.Buffer
   303  			restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buf)
   304  			if err := elem.ExpAsVar.Expr.Restore(restoreCtx); err != nil {
   305  				return err
   306  			}
   307  			colName = model.NewCIStr(buf.String())
   308  		} else {
   309  			colName = elem.ExpAsVar.AsName
   310  		}
   311  
   312  		cols = append(cols, ResultColumn{
   313  			Name: colName,
   314  			Type: expr.ReturnType(),
   315  		})
   316  
   317  	}
   318  	proj.SetColumns(cols)
   319  	proj.SetChildren(plan)
   320  
   321  	b.setPlan(proj)
   322  	return nil
   323  }
   324  
   325  func (b *Builder) buildMatch(matches []*ast.MatchClause) (LogicalPlan, error) {
   326  	if len(matches) == 0 {
   327  		return &LogicalDual{}, nil
   328  	}
   329  
   330  	sgb := NewSubgraphBuilder(b.sc.CurrentGraph())
   331  	for _, match := range matches {
   332  		sgb.AddPathPatterns(match.Paths...)
   333  	}
   334  	sg, err := sgb.Build()
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	cols := ResultColumnsFromSubgraph(sg)
   340  	plan := &LogicalMatch{Subgraph: sg}
   341  	plan.SetColumns(cols)
   342  
   343  	return plan, nil
   344  }
   345  
   346  func (b *Builder) buildShow(stmt *ast.ShowStmt) error {
   347  	plan := &Simple{
   348  		Statement: stmt,
   349  	}
   350  	b.setPlan(plan)
   351  	return nil
   352  }