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