github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optbuilder/orderby.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package optbuilder 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/sql/opt" 15 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 16 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 17 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 18 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/sql/types" 21 ) 22 23 // analyzeOrderBy analyzes an Ordering physical property from the ORDER BY 24 // clause and adds the resulting typed expressions to orderByScope. 25 func (b *Builder) analyzeOrderBy( 26 orderBy tree.OrderBy, inScope, projectionsScope *scope, 27 ) (orderByScope *scope) { 28 if orderBy == nil { 29 return nil 30 } 31 32 orderByScope = inScope.push() 33 orderByScope.cols = make([]scopeColumn, 0, len(orderBy)) 34 35 // We need to save and restore the previous value of the field in 36 // semaCtx in case we are recursively called within a subquery 37 // context. 38 defer b.semaCtx.Properties.Restore(b.semaCtx.Properties) 39 b.semaCtx.Properties.Require(exprKindOrderBy.String(), tree.RejectGenerators) 40 inScope.context = exprKindOrderBy 41 42 for i := range orderBy { 43 b.analyzeOrderByArg(orderBy[i], inScope, projectionsScope, orderByScope) 44 } 45 return orderByScope 46 } 47 48 // buildOrderBy builds an Ordering physical property from the ORDER BY clause. 49 // ORDER BY is not a relational expression, but instead a required physical 50 // property on the output. 51 // 52 // Since the ordering property can only refer to output columns, we may need 53 // to add a projection for the ordering columns. For example, consider the 54 // following query: 55 // SELECT a FROM t ORDER BY c 56 // The `c` column must be retained in the projection (and the presentation 57 // property then omits it). 58 // 59 // buildOrderBy builds a set of memo groups for any ORDER BY columns that are 60 // not already present in the SELECT list (as represented by the initial set 61 // of columns in projectionsScope). buildOrderBy adds these new ORDER BY 62 // columns to the projectionsScope and sets the ordering property on the 63 // projectionsScope. This property later becomes part of the required physical 64 // properties returned by Build. 65 func (b *Builder) buildOrderBy(inScope, projectionsScope, orderByScope *scope) { 66 if orderByScope == nil { 67 return 68 } 69 70 orderByScope.ordering = make([]opt.OrderingColumn, 0, len(orderByScope.cols)) 71 72 for i := range orderByScope.cols { 73 b.buildOrderByArg(inScope, projectionsScope, orderByScope, &orderByScope.cols[i]) 74 } 75 76 projectionsScope.setOrdering(orderByScope.cols, orderByScope.ordering) 77 } 78 79 // findIndexByName returns an index in the table with the given name. If the 80 // name is empty the primary index is returned. 81 func (b *Builder) findIndexByName(table cat.Table, name tree.UnrestrictedName) (cat.Index, error) { 82 if name == "" { 83 return table.Index(0), nil 84 } 85 86 for i, n := 0, table.IndexCount(); i < n; i++ { 87 idx := table.Index(i) 88 if tree.Name(name) == idx.Name() { 89 return idx, nil 90 } 91 } 92 93 return nil, pgerror.Newf(pgcode.UndefinedObject, 94 `index %q not found`, name) 95 } 96 97 // addOrderByOrDistinctOnColumn builds extraCol.expr as a column in extraColsScope; if it is 98 // already projected in projectionsScope then that projection is re-used. 99 func (b *Builder) addOrderByOrDistinctOnColumn( 100 inScope, projectionsScope, extraColsScope *scope, extraCol *scopeColumn, 101 ) { 102 // Use an existing projection if possible (even if it has side-effects; see 103 // the SQL99 rules described in analyzeExtraArgument). Otherwise, build a new 104 // projection. 105 if col := projectionsScope.findExistingCol( 106 extraCol.getExpr(), 107 true, /* allowSideEffects */ 108 ); col != nil { 109 extraCol.id = col.id 110 } else { 111 b.buildScalar(extraCol.getExpr(), inScope, extraColsScope, extraCol, nil) 112 } 113 } 114 115 // analyzeOrderByIndex appends to the orderByScope a column for each indexed 116 // column in the specified index, including the implicit primary key columns. 117 func (b *Builder) analyzeOrderByIndex( 118 order *tree.Order, inScope, projectionsScope, orderByScope *scope, 119 ) { 120 tab, tn := b.resolveTable(&order.Table, privilege.SELECT) 121 index, err := b.findIndexByName(tab, order.Index) 122 if err != nil { 123 panic(err) 124 } 125 126 // We fully qualify the table name in case another table expression was 127 // aliased to the same name as an existing table. 128 tn.ExplicitCatalog = true 129 tn.ExplicitSchema = true 130 131 // Append each key column from the index (including the implicit primary key 132 // columns) to the ordering scope. 133 for i, n := 0, index.KeyColumnCount(); i < n; i++ { 134 // Columns which are indexable are always orderable. 135 col := index.Column(i) 136 if err != nil { 137 panic(err) 138 } 139 140 desc := col.Descending 141 142 // DESC inverts the order of the index. 143 if order.Direction == tree.Descending { 144 desc = !desc 145 } 146 147 colItem := tree.NewColumnItem(&tn, col.ColName()) 148 expr := inScope.resolveType(colItem, types.Any) 149 outCol := b.addColumn(orderByScope, "" /* alias */, expr) 150 outCol.descending = desc 151 } 152 } 153 154 // analyzeOrderByArg analyzes a single ORDER BY argument. Typically this is a 155 // single column, with the exception of qualified star "table.*". The resulting 156 // typed expression(s) are added to orderByScope. 157 func (b *Builder) analyzeOrderByArg( 158 order *tree.Order, inScope, projectionsScope, orderByScope *scope, 159 ) { 160 if order.OrderType == tree.OrderByIndex { 161 b.analyzeOrderByIndex(order, inScope, projectionsScope, orderByScope) 162 return 163 } 164 165 // Analyze the ORDER BY column(s). 166 start := len(orderByScope.cols) 167 b.analyzeExtraArgument(order.Expr, inScope, projectionsScope, orderByScope) 168 for i := start; i < len(orderByScope.cols); i++ { 169 col := &orderByScope.cols[i] 170 col.descending = order.Direction == tree.Descending 171 } 172 } 173 174 // buildOrderByArg sets up the projection of a single ORDER BY argument. 175 // The projection column is built in the orderByScope and used to build 176 // an ordering on the same scope. 177 func (b *Builder) buildOrderByArg( 178 inScope, projectionsScope, orderByScope *scope, orderByCol *scopeColumn, 179 ) { 180 // Build the ORDER BY column. 181 b.addOrderByOrDistinctOnColumn(inScope, projectionsScope, orderByScope, orderByCol) 182 183 // Add the new column to the ordering. 184 orderByScope.ordering = append(orderByScope.ordering, 185 opt.MakeOrderingColumn(orderByCol.id, orderByCol.descending), 186 ) 187 } 188 189 // analyzeExtraArgument analyzes a single ORDER BY or DISTINCT ON argument. 190 // Typically this is a single column, with the exception of qualified star 191 // (table.*). The resulting typed expression(s) are added to extraColsScope. 192 func (b *Builder) analyzeExtraArgument( 193 expr tree.Expr, inScope, projectionsScope, extraColsScope *scope, 194 ) { 195 // Unwrap parenthesized expressions like "((a))" to "a". 196 expr = tree.StripParens(expr) 197 198 // The logical data source for ORDER BY or DISTINCT ON is the list of column 199 // expressions for a SELECT, as specified in the input SQL text (or an entire 200 // UNION or VALUES clause). Alas, SQL has some historical baggage from SQL92 201 // and there are some special cases: 202 // 203 // SQL92 rules: 204 // 205 // 1) if the expression is the aliased (AS) name of an 206 // expression in a SELECT clause, then use that 207 // expression as sort key. 208 // e.g. SELECT a AS b, b AS c ORDER BY b 209 // this sorts on the first column. 210 // 211 // 2) column ordinals. If a simple integer literal is used, 212 // optionally enclosed within parentheses but *not subject to 213 // any arithmetic*, then this refers to one of the columns of 214 // the data source. Then use the SELECT expression at that 215 // ordinal position as sort key. 216 // 217 // SQL99 rules: 218 // 219 // 3) otherwise, if the expression is already in the SELECT list, 220 // then use that expression as sort key. 221 // e.g. SELECT b AS c ORDER BY b 222 // this sorts on the first column. 223 // (this is an optimization) 224 // 225 // 4) if the sort key is not dependent on the data source (no 226 // IndexedVar) then simply do not sort. (this is an optimization) 227 // 228 // 5) otherwise, add a new projection with the ORDER BY expression 229 // and use that as sort key. 230 // e.g. SELECT a FROM t ORDER by b 231 // e.g. SELECT a, b FROM t ORDER by a+b 232 233 // First, deal with projection aliases. 234 idx := colIdxByProjectionAlias(expr, inScope.context.String(), projectionsScope) 235 236 // If the expression does not refer to an alias, deal with 237 // column ordinals. 238 if idx == -1 { 239 idx = colIndex(len(projectionsScope.cols), expr, inScope.context.String()) 240 } 241 242 var exprs tree.TypedExprs 243 if idx != -1 { 244 exprs = []tree.TypedExpr{projectionsScope.cols[idx].getExpr()} 245 } else { 246 exprs = b.expandStarAndResolveType(expr, inScope) 247 248 // ORDER BY (a, b) -> ORDER BY a, b 249 exprs = flattenTuples(exprs) 250 } 251 252 for _, e := range exprs { 253 // Ensure we can order on the given column(s). 254 ensureColumnOrderable(e) 255 b.addColumn(extraColsScope, "" /* alias */, e) 256 } 257 } 258 259 func ensureColumnOrderable(e tree.TypedExpr) { 260 typ := e.ResolvedType() 261 if typ.Family() == types.JsonFamily || 262 (typ.Family() == types.ArrayFamily && typ.ArrayContents().Family() == types.JsonFamily) { 263 panic(unimplementedWithIssueDetailf(35706, "", "can't order by column type jsonb")) 264 } 265 }