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  }