github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/expr_name_gen.go (about)

     1  // Copyright 2019 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 memo
    12  
    13  import (
    14  	"fmt"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical"
    19  )
    20  
    21  // ExprNameGenerator is used to generate a unique name for each relational
    22  // expression in a query tree. See GenerateName for details.
    23  type ExprNameGenerator struct {
    24  	prefix    string
    25  	exprCount int
    26  }
    27  
    28  // NewExprNameGenerator creates a new instance of ExprNameGenerator,
    29  // initialized with the given prefix.
    30  func NewExprNameGenerator(prefix string) *ExprNameGenerator {
    31  	return &ExprNameGenerator{prefix: prefix}
    32  }
    33  
    34  // GenerateName generates a name for a relational expression with the given
    35  // operator. It is used to generate names for each relational expression
    36  // in a query tree, corresponding to the tables that will be created if the
    37  // session variable `save_tables_prefix` is non-empty.
    38  //
    39  // Each invocation of GenerateName is guaranteed to produce a unique name for
    40  // a given instance of ExprNameGenerator. This works because each name is
    41  // appended with a unique, auto-incrementing number. For readability, the
    42  // generated names also contain a common prefix and the name of the relational
    43  // operator separated with underscores. For example: my_query_scan_2.
    44  //
    45  // Since the names are generated with an auto-incrementing number, the order
    46  // of invocation is important. For a given query, the number assigned to each
    47  // relational subexpression corresponds to the order in which the expression
    48  // was encountered during tree traversal. Thus, in order to generate a
    49  // consistent name, always call GenerateName in a pre-order traversal of the
    50  // expression tree.
    51  //
    52  func (g *ExprNameGenerator) GenerateName(op opt.Operator) string {
    53  	// Replace all instances of "-" in the operator name with "_" in order to
    54  	// create a legal table name.
    55  	operator := strings.Replace(op.String(), "-", "_", -1)
    56  	g.exprCount++
    57  	return fmt.Sprintf("%s_%s_%d", g.prefix, operator, g.exprCount)
    58  }
    59  
    60  // ColumnNameGenerator is used to generate a unique name for each column of a
    61  // relational expression. See GenerateName for details.
    62  type ColumnNameGenerator struct {
    63  	e    RelExpr
    64  	pres physical.Presentation
    65  	seen map[string]int
    66  }
    67  
    68  // NewColumnNameGenerator creates a new instance of ColumnNameGenerator,
    69  // initialized with the given relational expression.
    70  func NewColumnNameGenerator(e RelExpr) *ColumnNameGenerator {
    71  	return &ColumnNameGenerator{
    72  		e:    e,
    73  		pres: e.RequiredPhysical().Presentation,
    74  		seen: make(map[string]int, e.Relational().OutputCols.Len()),
    75  	}
    76  }
    77  
    78  // GenerateName generates a unique name for each column in a relational
    79  // expression. This function is used to generate consistent, unique names
    80  // for the columns in the table that will be created if the session
    81  // variable `save_tables_prefix` is non-empty.
    82  func (g *ColumnNameGenerator) GenerateName(col opt.ColumnID) string {
    83  	colMeta := g.e.Memo().Metadata().ColumnMeta(col)
    84  	colName := colMeta.Alias
    85  
    86  	// Check whether the presentation has a different name for this column, and
    87  	// use it if available.
    88  	for i := range g.pres {
    89  		if g.pres[i].ID == col {
    90  			colName = g.pres[i].Alias
    91  			break
    92  		}
    93  	}
    94  
    95  	// Every column name must be unique.
    96  	if cnt, ok := g.seen[colName]; ok {
    97  		g.seen[colName]++
    98  		colName = fmt.Sprintf("%s_%d", colName, cnt)
    99  	} else {
   100  		g.seen[colName] = 1
   101  	}
   102  
   103  	return colName
   104  }