github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/group_by.go (about) 1 // Copyright 2020-2021 Dolthub, Inc. 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 "fmt" 19 "strings" 20 21 errors "gopkg.in/src-d/go-errors.v1" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/expression" 25 ) 26 27 // ErrGroupBy is returned when the aggregation is not supported. 28 var ErrGroupBy = errors.NewKind("group by aggregation '%v' not supported") 29 30 // GroupBy groups the rows by some expressions. 31 type GroupBy struct { 32 UnaryNode 33 SelectedExprs []sql.Expression 34 GroupByExprs []sql.Expression 35 } 36 37 var _ sql.Expressioner = (*GroupBy)(nil) 38 var _ sql.Node = (*GroupBy)(nil) 39 var _ sql.Projector = (*GroupBy)(nil) 40 var _ sql.CollationCoercible = (*GroupBy)(nil) 41 42 // NewGroupBy creates a new GroupBy node. Like Project, GroupBy is a top-level node, and contains all the fields that 43 // will appear in the output of the query. Some of these fields may be aggregate functions, some may be columns or 44 // other expressions. Unlike a project, the GroupBy also has a list of group-by expressions, which usually also appear 45 // in the list of selected expressions. 46 func NewGroupBy(selectedExprs, groupByExprs []sql.Expression, child sql.Node) *GroupBy { 47 return &GroupBy{ 48 UnaryNode: UnaryNode{Child: child}, 49 SelectedExprs: selectedExprs, 50 GroupByExprs: groupByExprs, 51 } 52 } 53 54 // Resolved implements the Resolvable interface. 55 func (g *GroupBy) Resolved() bool { 56 return g.UnaryNode.Child.Resolved() && 57 expression.ExpressionsResolved(g.SelectedExprs...) && 58 expression.ExpressionsResolved(g.GroupByExprs...) 59 } 60 61 func (g *GroupBy) IsReadOnly() bool { 62 return g.Child.IsReadOnly() 63 } 64 65 // Schema implements the Node interface. 66 func (g *GroupBy) Schema() sql.Schema { 67 var s = make(sql.Schema, len(g.SelectedExprs)) 68 for i, e := range g.SelectedExprs { 69 var name string 70 if n, ok := e.(sql.Nameable); ok { 71 name = n.Name() 72 } else { 73 name = AliasSubqueryString(e) 74 } 75 76 var table string 77 if t, ok := e.(sql.Tableable); ok { 78 table = t.Table() 79 } 80 81 var db string 82 if t, ok := e.(sql.Databaseable); ok { 83 db = t.Database() 84 } 85 86 s[i] = &sql.Column{ 87 Name: name, 88 Type: e.Type(), 89 Nullable: e.IsNullable(), 90 Source: table, 91 DatabaseSource: db, 92 } 93 } 94 95 return s 96 } 97 98 // WithChildren implements the Node interface. 99 func (g *GroupBy) WithChildren(children ...sql.Node) (sql.Node, error) { 100 if len(children) != 1 { 101 return nil, sql.ErrInvalidChildrenNumber.New(g, len(children), 1) 102 } 103 104 return NewGroupBy(g.SelectedExprs, g.GroupByExprs, children[0]), nil 105 } 106 107 // CheckPrivileges implements the interface sql.Node. 108 func (g *GroupBy) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { 109 return g.Child.CheckPrivileges(ctx, opChecker) 110 } 111 112 // CollationCoercibility implements the interface sql.CollationCoercible. 113 func (g *GroupBy) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 114 return sql.GetCoercibility(ctx, g.Child) 115 } 116 117 // WithExpressions implements the Node interface. 118 func (g *GroupBy) WithExpressions(exprs ...sql.Expression) (sql.Node, error) { 119 expected := len(g.SelectedExprs) + len(g.GroupByExprs) 120 if len(exprs) != expected { 121 return nil, sql.ErrInvalidChildrenNumber.New(g, len(exprs), expected) 122 } 123 124 agg := make([]sql.Expression, len(g.SelectedExprs)) 125 copy(agg, exprs[:len(g.SelectedExprs)]) 126 127 grouping := make([]sql.Expression, len(g.GroupByExprs)) 128 copy(grouping, exprs[len(g.SelectedExprs):]) 129 130 return NewGroupBy(agg, grouping, g.Child), nil 131 } 132 133 func (g *GroupBy) String() string { 134 pr := sql.NewTreePrinter() 135 _ = pr.WriteNode("GroupBy") 136 137 var selectedExprs = make([]string, len(g.SelectedExprs)) 138 for i, e := range g.SelectedExprs { 139 selectedExprs[i] = e.String() 140 } 141 142 var grouping = make([]string, len(g.GroupByExprs)) 143 for i, g := range g.GroupByExprs { 144 grouping[i] = g.String() 145 } 146 147 _ = pr.WriteChildren( 148 fmt.Sprintf("SelectedExprs(%s)", strings.Join(selectedExprs, ", ")), 149 fmt.Sprintf("Grouping(%s)", strings.Join(grouping, ", ")), 150 g.Child.String(), 151 ) 152 return pr.String() 153 } 154 155 func (g *GroupBy) DebugString() string { 156 pr := sql.NewTreePrinter() 157 _ = pr.WriteNode("GroupBy") 158 159 var selectedExprs = make([]string, len(g.SelectedExprs)) 160 for i, e := range g.SelectedExprs { 161 selectedExprs[i] = sql.DebugString(e) 162 } 163 164 var grouping = make([]string, len(g.GroupByExprs)) 165 for i, g := range g.GroupByExprs { 166 grouping[i] = sql.DebugString(g) 167 } 168 169 _ = pr.WriteChildren( 170 fmt.Sprintf("select: %s", strings.Join(selectedExprs, ", ")), 171 fmt.Sprintf("group: %s", strings.Join(grouping, ", ")), 172 sql.DebugString(g.Child), 173 ) 174 return pr.String() 175 } 176 177 // Expressions implements the Expressioner interface. 178 func (g *GroupBy) Expressions() []sql.Expression { 179 var exprs []sql.Expression 180 exprs = append(exprs, g.SelectedExprs...) 181 exprs = append(exprs, g.GroupByExprs...) 182 return exprs 183 } 184 185 // ProjectedExprs implements the sql.Projector interface 186 func (g *GroupBy) ProjectedExprs() []sql.Expression { 187 return g.SelectedExprs 188 }