github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/having_binder.go (about)

     1  // Copyright 2022 Matrix Origin
     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 plan
    16  
    17  import (
    18  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    19  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    20  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    21  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    22  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function"
    23  )
    24  
    25  func NewHavingBinder(builder *QueryBuilder, ctx *BindContext) *HavingBinder {
    26  	b := &HavingBinder{
    27  		insideAgg: false,
    28  	}
    29  	b.sysCtx = builder.GetContext()
    30  	b.builder = builder
    31  	b.ctx = ctx
    32  	b.impl = b
    33  
    34  	return b
    35  }
    36  
    37  func (b *HavingBinder) BindExpr(astExpr tree.Expr, depth int32, isRoot bool) (*plan.Expr, error) {
    38  	astStr := tree.String(astExpr, dialect.MYSQL)
    39  
    40  	if !b.insideAgg {
    41  		if colPos, ok := b.ctx.groupByAst[astStr]; ok {
    42  			return &plan.Expr{
    43  				Typ: b.ctx.groups[colPos].Typ,
    44  				Expr: &plan.Expr_Col{
    45  					Col: &plan.ColRef{
    46  						RelPos: b.ctx.groupTag,
    47  						ColPos: colPos,
    48  					},
    49  				},
    50  			}, nil
    51  		}
    52  	}
    53  
    54  	if colPos, ok := b.ctx.aggregateByAst[astStr]; ok {
    55  		if !b.insideAgg {
    56  			return &plan.Expr{
    57  				Typ: b.ctx.aggregates[colPos].Typ,
    58  				Expr: &plan.Expr_Col{
    59  					Col: &plan.ColRef{
    60  						RelPos: b.ctx.aggregateTag,
    61  						ColPos: colPos,
    62  					},
    63  				},
    64  			}, nil
    65  		} else {
    66  			return nil, moerr.NewInvalidInput(b.GetContext(), "nestted aggregate function")
    67  		}
    68  	}
    69  
    70  	return b.baseBindExpr(astExpr, depth, isRoot)
    71  }
    72  
    73  func (b *HavingBinder) BindColRef(astExpr *tree.UnresolvedName, depth int32, isRoot bool) (*plan.Expr, error) {
    74  	if b.insideAgg {
    75  		expr, err := b.baseBindColRef(astExpr, depth, isRoot)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  
    80  		if _, ok := expr.Expr.(*plan.Expr_Corr); ok {
    81  			return nil, moerr.NewNYI(b.GetContext(), "correlated columns in aggregate function")
    82  		}
    83  
    84  		return expr, nil
    85  	} else if b.builder.mysqlCompatible {
    86  		expr, err := b.baseBindColRef(astExpr, depth, isRoot)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  
    91  		if _, ok := expr.Expr.(*plan.Expr_Corr); ok {
    92  			return nil, moerr.NewNYI(b.GetContext(), "correlated columns in aggregate function")
    93  		}
    94  
    95  		newExpr, _ := bindFuncExprImplByPlanExpr(b.builder.compCtx.GetContext(), "any_value", []*plan.Expr{expr})
    96  		colPos := len(b.ctx.aggregates)
    97  		b.ctx.aggregates = append(b.ctx.aggregates, newExpr)
    98  		return &plan.Expr{
    99  			Typ: b.ctx.aggregates[colPos].Typ,
   100  			Expr: &plan.Expr_Col{
   101  				Col: &plan.ColRef{
   102  					RelPos: b.ctx.aggregateTag,
   103  					ColPos: int32(colPos),
   104  				},
   105  			},
   106  		}, nil
   107  	} else {
   108  		return nil, moerr.NewSyntaxError(b.GetContext(), "column %q must appear in the GROUP BY clause or be used in an aggregate function", tree.String(astExpr, dialect.MYSQL))
   109  	}
   110  }
   111  
   112  func (b *HavingBinder) BindAggFunc(funcName string, astExpr *tree.FuncExpr, depth int32, isRoot bool) (*plan.Expr, error) {
   113  	if b.insideAgg {
   114  		return nil, moerr.NewSyntaxError(b.GetContext(), "aggregate function %s calls cannot be nested", funcName)
   115  	}
   116  
   117  	b.insideAgg = true
   118  	expr, err := b.bindFuncExprImplByAstExpr(funcName, astExpr.Exprs, depth)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	if astExpr.Type == tree.FUNC_TYPE_DISTINCT {
   123  		expr.GetF().Func.Obj = int64(int64(uint64(expr.GetF().Func.Obj) | function.Distinct))
   124  	}
   125  	b.insideAgg = false
   126  
   127  	colPos := int32(len(b.ctx.aggregates))
   128  	astStr := tree.String(astExpr, dialect.MYSQL)
   129  	b.ctx.aggregateByAst[astStr] = colPos
   130  	b.ctx.aggregates = append(b.ctx.aggregates, expr)
   131  
   132  	return &plan.Expr{
   133  		Typ: expr.Typ,
   134  		Expr: &plan.Expr_Col{
   135  			Col: &plan.ColRef{
   136  				RelPos: b.ctx.aggregateTag,
   137  				ColPos: colPos,
   138  			},
   139  		},
   140  	}, nil
   141  }
   142  
   143  func (b *HavingBinder) BindWinFunc(funcName string, astExpr *tree.FuncExpr, depth int32, isRoot bool) (*plan.Expr, error) {
   144  	if b.insideAgg {
   145  		return nil, moerr.NewSyntaxError(b.GetContext(), "aggregate function calls cannot contain window function calls")
   146  	} else {
   147  		return nil, moerr.NewSyntaxError(b.GetContext(), "window %s functions not allowed in having clause", funcName)
   148  	}
   149  }
   150  
   151  func (b *HavingBinder) BindSubquery(astExpr *tree.Subquery, isRoot bool) (*plan.Expr, error) {
   152  	return b.baseBindSubquery(astExpr, isRoot)
   153  }