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 }