github.com/matrixorigin/matrixone@v1.2.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  	"go/constant"
    19  
    20  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    21  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    22  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    23  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    24  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function"
    25  )
    26  
    27  func NewHavingBinder(builder *QueryBuilder, ctx *BindContext) *HavingBinder {
    28  	b := &HavingBinder{
    29  		insideAgg: false,
    30  	}
    31  	b.sysCtx = builder.GetContext()
    32  	b.builder = builder
    33  	b.ctx = ctx
    34  	b.impl = b
    35  
    36  	return b
    37  }
    38  
    39  func (b *HavingBinder) BindExpr(astExpr tree.Expr, depth int32, isRoot bool) (*plan.Expr, error) {
    40  	astStr := tree.String(astExpr, dialect.MYSQL)
    41  
    42  	if !b.insideAgg {
    43  		if colPos, ok := b.ctx.groupByAst[astStr]; ok {
    44  			return &plan.Expr{
    45  				Typ: b.ctx.groups[colPos].Typ,
    46  				Expr: &plan.Expr_Col{
    47  					Col: &plan.ColRef{
    48  						RelPos: b.ctx.groupTag,
    49  						ColPos: colPos,
    50  					},
    51  				},
    52  			}, nil
    53  		}
    54  	}
    55  
    56  	if colPos, ok := b.ctx.aggregateByAst[astStr]; ok {
    57  		if !b.insideAgg {
    58  			return &plan.Expr{
    59  				Typ: b.ctx.aggregates[colPos].Typ,
    60  				Expr: &plan.Expr_Col{
    61  					Col: &plan.ColRef{
    62  						RelPos: b.ctx.aggregateTag,
    63  						ColPos: colPos,
    64  					},
    65  				},
    66  			}, nil
    67  		} else {
    68  			return nil, moerr.NewInvalidInput(b.GetContext(), "nestted aggregate function")
    69  		}
    70  	}
    71  
    72  	if colPos, ok := b.ctx.sampleByAst[astStr]; ok {
    73  		return &plan.Expr{
    74  			Typ: b.ctx.sampleFunc.columns[colPos].Typ,
    75  			Expr: &plan.Expr_Col{
    76  				Col: &plan.ColRef{
    77  					RelPos: b.ctx.sampleTag,
    78  					ColPos: colPos,
    79  				},
    80  			},
    81  		}, nil
    82  	}
    83  
    84  	return b.baseBindExpr(astExpr, depth, isRoot)
    85  }
    86  
    87  func (b *HavingBinder) BindColRef(astExpr *tree.UnresolvedName, depth int32, isRoot bool) (*plan.Expr, error) {
    88  	if b.insideAgg {
    89  		expr, err := b.baseBindColRef(astExpr, depth, isRoot)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  
    94  		if _, ok := expr.Expr.(*plan.Expr_Corr); ok {
    95  			return nil, moerr.NewNYI(b.GetContext(), "correlated columns in aggregate function")
    96  		}
    97  
    98  		return expr, nil
    99  	} else if b.builder.mysqlCompatible {
   100  		expr, err := b.baseBindColRef(astExpr, depth, isRoot)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  
   105  		if _, ok := expr.Expr.(*plan.Expr_Corr); ok {
   106  			return nil, moerr.NewNYI(b.GetContext(), "correlated columns in aggregate function")
   107  		}
   108  
   109  		newExpr, _ := BindFuncExprImplByPlanExpr(b.builder.compCtx.GetContext(), "any_value", []*plan.Expr{expr})
   110  		colPos := len(b.ctx.aggregates)
   111  		b.ctx.aggregates = append(b.ctx.aggregates, newExpr)
   112  		return &plan.Expr{
   113  			Typ: b.ctx.aggregates[colPos].Typ,
   114  			Expr: &plan.Expr_Col{
   115  				Col: &plan.ColRef{
   116  					RelPos: b.ctx.aggregateTag,
   117  					ColPos: int32(colPos),
   118  				},
   119  			},
   120  		}, nil
   121  	} else {
   122  		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))
   123  	}
   124  }
   125  
   126  func (b *HavingBinder) BindAggFunc(funcName string, astExpr *tree.FuncExpr, depth int32, isRoot bool) (*plan.Expr, error) {
   127  	if b.insideAgg {
   128  		return nil, moerr.NewSyntaxError(b.GetContext(), "aggregate function %s calls cannot be nested", funcName)
   129  	}
   130  
   131  	if funcName == NameGroupConcat {
   132  		err := b.processForceWindows(funcName, astExpr, depth, isRoot)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  	}
   137  
   138  	b.insideAgg = true
   139  	expr, err := b.bindFuncExprImplByAstExpr(funcName, astExpr.Exprs, depth)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	if astExpr.Type == tree.FUNC_TYPE_DISTINCT {
   144  		if funcName != "max" && funcName != "min" && funcName != "any_value" {
   145  			expr.GetF().Func.Obj = int64(uint64(expr.GetF().Func.Obj) | function.Distinct)
   146  		}
   147  	}
   148  	b.insideAgg = false
   149  
   150  	colPos := int32(len(b.ctx.aggregates))
   151  	astStr := tree.String(astExpr, dialect.MYSQL)
   152  	b.ctx.aggregateByAst[astStr] = colPos
   153  	b.ctx.aggregates = append(b.ctx.aggregates, expr)
   154  
   155  	return &plan.Expr{
   156  		Typ: expr.Typ,
   157  		Expr: &plan.Expr_Col{
   158  			Col: &plan.ColRef{
   159  				RelPos: b.ctx.aggregateTag,
   160  				ColPos: colPos,
   161  			},
   162  		},
   163  	}, nil
   164  }
   165  
   166  func (b *HavingBinder) processForceWindows(funcName string, astExpr *tree.FuncExpr, depth int32, isRoot bool) error {
   167  
   168  	if len(astExpr.OrderBy) < 1 {
   169  		return nil
   170  	}
   171  	b.ctx.forceWindows = true
   172  	b.ctx.isDistinct = true
   173  
   174  	w := &plan.WindowSpec{}
   175  	ws := &tree.WindowSpec{}
   176  
   177  	// window function
   178  	w.Name = funcName
   179  
   180  	// partition by
   181  	w.PartitionBy = DeepCopyExprList(b.ctx.groups)
   182  
   183  	//order by
   184  	w.OrderBy = make([]*plan.OrderBySpec, 0, len(astExpr.OrderBy))
   185  
   186  	for _, order := range astExpr.OrderBy {
   187  		orderExpr := order.Expr
   188  		if numVal, ok := order.Expr.(*tree.NumVal); ok {
   189  			switch numVal.Value.Kind() {
   190  			case constant.Int:
   191  				colPos, _ := constant.Int64Val(numVal.Value)
   192  				if numVal.Negative() {
   193  					moerr.NewSyntaxError(b.GetContext(), "ORDER BY position %v is negative", colPos)
   194  				}
   195  				if colPos < 1 || int(colPos) > len(astExpr.Exprs)-1 {
   196  					return moerr.NewSyntaxError(b.GetContext(), "ORDER BY position %v is not in group_concat arguments", colPos)
   197  				}
   198  				orderExpr = astExpr.Exprs[colPos-1]
   199  			default:
   200  				return moerr.NewSyntaxError(b.GetContext(), "non-integer constant in ORDER BY")
   201  			}
   202  
   203  		}
   204  
   205  		if _, ok := order.Expr.(*tree.Subquery); ok {
   206  			return moerr.NewNotSupported(b.GetContext(), "subquery in group_concat ORDER BY")
   207  		}
   208  
   209  		b.insideAgg = true
   210  		expr, err := b.BindExpr(orderExpr, depth, isRoot)
   211  		b.insideAgg = false
   212  
   213  		if err != nil {
   214  			return err
   215  		}
   216  
   217  		orderBy := &plan.OrderBySpec{
   218  			Expr: expr,
   219  			Flag: plan.OrderBySpec_INTERNAL,
   220  		}
   221  
   222  		switch order.Direction {
   223  		case tree.Ascending:
   224  			orderBy.Flag |= plan.OrderBySpec_ASC
   225  		case tree.Descending:
   226  			orderBy.Flag |= plan.OrderBySpec_DESC
   227  		}
   228  
   229  		switch order.NullsPosition {
   230  		case tree.NullsFirst:
   231  			orderBy.Flag |= plan.OrderBySpec_NULLS_FIRST
   232  		case tree.NullsLast:
   233  			orderBy.Flag |= plan.OrderBySpec_NULLS_LAST
   234  		}
   235  
   236  		w.OrderBy = append(w.OrderBy, orderBy)
   237  	}
   238  
   239  	w.Frame = getFrame(ws)
   240  
   241  	// append
   242  	b.ctx.windows = append(b.ctx.windows, &plan.Expr{
   243  		Expr: &plan.Expr_W{W: w},
   244  	})
   245  
   246  	return nil
   247  }
   248  
   249  func getFrame(ws *tree.WindowSpec) *plan.FrameClause {
   250  
   251  	f := &tree.FrameClause{Type: tree.Range}
   252  
   253  	if ws.OrderBy == nil {
   254  		f.Start = &tree.FrameBound{Type: tree.Preceding, UnBounded: true}
   255  		f.End = &tree.FrameBound{Type: tree.Following, UnBounded: true}
   256  	} else {
   257  		f.Start = &tree.FrameBound{Type: tree.Preceding, UnBounded: true}
   258  		f.End = &tree.FrameBound{Type: tree.CurrentRow}
   259  	}
   260  
   261  	ws.HasFrame = false
   262  	ws.Frame = f
   263  
   264  	return &plan.FrameClause{
   265  		Type: plan.FrameClause_FrameType(ws.Frame.Type),
   266  		Start: &plan.FrameBound{
   267  			Type:      plan.FrameBound_BoundType(ws.Frame.Start.Type),
   268  			UnBounded: ws.Frame.Start.UnBounded,
   269  		},
   270  		End: &plan.FrameBound{
   271  			Type:      plan.FrameBound_BoundType(ws.Frame.End.Type),
   272  			UnBounded: ws.Frame.End.UnBounded,
   273  		},
   274  	}
   275  }
   276  
   277  func (b *HavingBinder) BindWinFunc(funcName string, astExpr *tree.FuncExpr, depth int32, isRoot bool) (*plan.Expr, error) {
   278  	if b.insideAgg {
   279  		return nil, moerr.NewSyntaxError(b.GetContext(), "aggregate function calls cannot contain window function calls")
   280  	} else {
   281  		return nil, moerr.NewSyntaxError(b.GetContext(), "window %s functions not allowed in having clause", funcName)
   282  	}
   283  }
   284  
   285  func (b *HavingBinder) BindSubquery(astExpr *tree.Subquery, isRoot bool) (*plan.Expr, error) {
   286  	return b.baseBindSubquery(astExpr, isRoot)
   287  }
   288  
   289  func (b *HavingBinder) BindTimeWindowFunc(funcName string, astExpr *tree.FuncExpr, depth int32, isRoot bool) (*plan.Expr, error) {
   290  	if astExpr.Type == tree.FUNC_TYPE_DISTINCT {
   291  		return nil, moerr.NewNotSupported(b.GetContext(), "DISTINCT in time window")
   292  	}
   293  
   294  	b.insideAgg = true
   295  	expr, err := b.bindFuncExprImplByAstExpr(funcName, astExpr.Exprs, depth)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	b.insideAgg = false
   300  
   301  	colPos := int32(len(b.ctx.times))
   302  	astStr := tree.String(astExpr, dialect.MYSQL)
   303  	b.ctx.timeByAst[astStr] = colPos
   304  	b.ctx.times = append(b.ctx.times, expr)
   305  	return &plan.Expr{
   306  		Typ: expr.Typ,
   307  		Expr: &plan.Expr_Col{
   308  			Col: &plan.ColRef{
   309  				RelPos: b.ctx.timeTag,
   310  				ColPos: colPos,
   311  			},
   312  		},
   313  	}, nil
   314  }