github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/col_name.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 tree
    12  
    13  import (
    14  	"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    16  )
    17  
    18  // GetRenderColName computes a name for a result column.
    19  // A name specified with AS takes priority, otherwise a name
    20  // is derived from the expression.
    21  //
    22  // This function is meant to be used on untransformed syntax trees.
    23  //
    24  // The algorithm is borrowed from FigureColName() in PostgreSQL 10, to be
    25  // found in src/backend/parser/parse_target.c. We reuse this algorithm
    26  // to provide names more compatible with PostgreSQL.
    27  func GetRenderColName(searchPath sessiondata.SearchPath, target SelectExpr) (string, error) {
    28  	if target.As != "" {
    29  		return string(target.As), nil
    30  	}
    31  
    32  	_, s, err := ComputeColNameInternal(searchPath, target.Expr)
    33  	if err != nil {
    34  		return s, err
    35  	}
    36  	if len(s) == 0 {
    37  		s = "?column?"
    38  	}
    39  	return s, nil
    40  }
    41  
    42  // ComputeColNameInternal is the workhorse for GetRenderColName.
    43  // The return value indicates the strength of the confidence in the result:
    44  // 0 - no information
    45  // 1 - second-best name choice
    46  // 2 - good name choice
    47  //
    48  // The algorithm is borrowed from FigureColnameInternal in PostgreSQL 10,
    49  // to be found in src/backend/parser/parse_target.c.
    50  func ComputeColNameInternal(sp sessiondata.SearchPath, target Expr) (int, string, error) {
    51  	// The order of the type cases below mirrors that of PostgreSQL's
    52  	// own code, so that code reviews can more easily compare the two
    53  	// implementations.
    54  	switch e := target.(type) {
    55  	case *UnresolvedName:
    56  		if e.Star {
    57  			return 0, "", nil
    58  		}
    59  		return 2, e.Parts[0], nil
    60  
    61  	case *ColumnItem:
    62  		return 2, e.Column(), nil
    63  
    64  	case *IndirectionExpr:
    65  		return ComputeColNameInternal(sp, e.Expr)
    66  
    67  	case *FuncExpr:
    68  		fd, err := e.Func.Resolve(sp)
    69  		if err != nil {
    70  			return 0, "", err
    71  		}
    72  		return 2, fd.Name, nil
    73  
    74  	case *NullIfExpr:
    75  		return 2, "nullif", nil
    76  
    77  	case *IfExpr:
    78  		return 2, "if", nil
    79  
    80  	case *ParenExpr:
    81  		return ComputeColNameInternal(sp, e.Expr)
    82  
    83  	case *CastExpr:
    84  		strength, s, err := ComputeColNameInternal(sp, e.Expr)
    85  		if err != nil {
    86  			return 0, "", err
    87  		}
    88  		if strength <= 1 {
    89  			if typ, ok := GetStaticallyKnownType(e.Type); ok {
    90  				return 0, computeCastName(typ), nil
    91  			}
    92  			return 1, e.Type.SQLString(), nil
    93  		}
    94  		return strength, s, nil
    95  
    96  	case *AnnotateTypeExpr:
    97  		// Ditto CastExpr.
    98  		strength, s, err := ComputeColNameInternal(sp, e.Expr)
    99  		if err != nil {
   100  			return 0, "", err
   101  		}
   102  		if strength <= 1 {
   103  			if typ, ok := GetStaticallyKnownType(e.Type); ok {
   104  				return 0, computeCastName(typ), nil
   105  			}
   106  			return 1, e.Type.SQLString(), nil
   107  		}
   108  		return strength, s, nil
   109  
   110  	case *CollateExpr:
   111  		return ComputeColNameInternal(sp, e.Expr)
   112  
   113  	case *ArrayFlatten:
   114  		return 2, "array", nil
   115  
   116  	case *Subquery:
   117  		if e.Exists {
   118  			return 2, "exists", nil
   119  		}
   120  		return computeColNameInternalSubquery(sp, e.Select)
   121  
   122  	case *CaseExpr:
   123  		strength, s, err := 0, "", error(nil)
   124  		if e.Else != nil {
   125  			strength, s, err = ComputeColNameInternal(sp, e.Else)
   126  		}
   127  		if strength <= 1 {
   128  			s = "case"
   129  			strength = 1
   130  		}
   131  		return strength, s, err
   132  
   133  	case *Array:
   134  		return 2, "array", nil
   135  
   136  	case *Tuple:
   137  		if e.Row {
   138  			return 2, "row", nil
   139  		}
   140  		if len(e.Exprs) == 1 {
   141  			if len(e.Labels) > 0 {
   142  				return 2, e.Labels[0], nil
   143  			}
   144  			return ComputeColNameInternal(sp, e.Exprs[0])
   145  		}
   146  
   147  	case *CoalesceExpr:
   148  		return 2, "coalesce", nil
   149  
   150  		// CockroachDB-specific nodes follow.
   151  	case *IfErrExpr:
   152  		if e.Else == nil {
   153  			return 2, "iserror", nil
   154  		}
   155  		return 2, "iferror", nil
   156  
   157  	case *ColumnAccessExpr:
   158  		return 2, e.ColName, nil
   159  
   160  	case *DBool:
   161  		// PostgreSQL implements the "true" and "false" literals
   162  		// by generating the expressions 't'::BOOL and 'f'::BOOL, so
   163  		// the derived column name is just "bool". Do the same.
   164  		return 1, "bool", nil
   165  	}
   166  
   167  	return 0, "", nil
   168  }
   169  
   170  // computeColNameInternalSubquery handles the cases of subqueries that
   171  // cannot be handled by the function above due to the Go typing
   172  // differences.
   173  func computeColNameInternalSubquery(
   174  	sp sessiondata.SearchPath, s SelectStatement,
   175  ) (int, string, error) {
   176  	switch e := s.(type) {
   177  	case *ParenSelect:
   178  		return computeColNameInternalSubquery(sp, e.Select.Select)
   179  	case *ValuesClause:
   180  		if len(e.Rows) > 0 && len(e.Rows[0]) == 1 {
   181  			return 2, "column1", nil
   182  		}
   183  	case *SelectClause:
   184  		if len(e.Exprs) == 1 {
   185  			if len(e.Exprs[0].As) > 0 {
   186  				return 2, string(e.Exprs[0].As), nil
   187  			}
   188  			return ComputeColNameInternal(sp, e.Exprs[0].Expr)
   189  		}
   190  	}
   191  	return 0, "", nil
   192  }
   193  
   194  // computeCastName returns the name manufactured by Postgres for a computed (or
   195  // annotated, in case of CRDB) column.
   196  func computeCastName(typ *types.T) string {
   197  	// Postgres uses the array element type name in case of array casts.
   198  	if typ.Family() == types.ArrayFamily {
   199  		typ = typ.ArrayContents()
   200  	}
   201  	return typ.PGName()
   202  
   203  }