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

     1  // Copyright 2020 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 schemaexpr
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    22  )
    23  
    24  // DequalifyColumnRefs returns an expression with database nad table names
    25  // stripped from qualified column names.
    26  //
    27  // For example:
    28  //
    29  //   tab.a > 0 AND db.tab.b = 'foo'
    30  //   =>
    31  //   a > 0 AND b = 'foo'
    32  //
    33  // This dequalification is necessary when CHECK constraints, computed columns,
    34  // or partial index predicates are created. If the table name was not stripped,
    35  // these expressions would become invalid if the table is renamed.
    36  func DequalifyColumnRefs(
    37  	ctx context.Context, source *sqlbase.DataSourceInfo, expr tree.Expr,
    38  ) (tree.Expr, error) {
    39  	resolver := sqlbase.ColumnResolver{Source: source}
    40  	return tree.SimpleVisit(
    41  		expr,
    42  		func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
    43  			if vBase, ok := expr.(tree.VarName); ok {
    44  				v, err := vBase.NormalizeVarName()
    45  				if err != nil {
    46  					return false, nil, err
    47  				}
    48  				if c, ok := v.(*tree.ColumnItem); ok {
    49  					_, err := c.Resolve(ctx, &resolver)
    50  					if err != nil {
    51  						return false, nil, err
    52  					}
    53  					colIdx := resolver.ResolverState.ColIdx
    54  					col := source.SourceColumns[colIdx]
    55  					return false, &tree.ColumnItem{ColumnName: tree.Name(col.Name)}, nil
    56  				}
    57  			}
    58  			return true, expr, err
    59  		},
    60  	)
    61  }
    62  
    63  // iterColDescriptors iterates over the expression's variable columns and
    64  // calls f on each.
    65  //
    66  // If the expression references a column that does not exist in the table
    67  // descriptor, iterColDescriptors errs with pgcode.UndefinedColumn.
    68  func iterColDescriptors(
    69  	desc *sqlbase.MutableTableDescriptor, rootExpr tree.Expr, f func(*sqlbase.ColumnDescriptor) error,
    70  ) error {
    71  	_, err := tree.SimpleVisit(rootExpr, func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
    72  		vBase, ok := expr.(tree.VarName)
    73  		if !ok {
    74  			// Not a VarName, don't do anything to this node.
    75  			return true, expr, nil
    76  		}
    77  
    78  		v, err := vBase.NormalizeVarName()
    79  		if err != nil {
    80  			return false, nil, err
    81  		}
    82  
    83  		c, ok := v.(*tree.ColumnItem)
    84  		if !ok {
    85  			return true, expr, nil
    86  		}
    87  
    88  		col, dropped, err := desc.FindColumnByName(c.ColumnName)
    89  		if err != nil || dropped {
    90  			return false, nil, pgerror.Newf(pgcode.UndefinedColumn,
    91  				"column %q does not exist, referenced in %q", c.ColumnName, rootExpr.String())
    92  		}
    93  
    94  		if err := f(col); err != nil {
    95  			return false, nil, err
    96  		}
    97  		return false, expr, err
    98  	})
    99  
   100  	return err
   101  }
   102  
   103  // DeserializeTableDescExpr takes in a serialized expression and a table, and
   104  // returns an expression that has all user defined types resolved for
   105  // formatting. It is intended to be used when displaying a serialized
   106  // expression within a TableDescriptor. For example, a DEFAULT expression
   107  // of a table containing a user defined type t with id 50 would have all
   108  // references to t replaced with the id 50. In order to display this expression
   109  // back to an end user, the serialized id 50 needs to be replaced back with t.
   110  // DeserializeTableDescExpr performs this logic, but only returns a
   111  // tree.Expr to be clear that these returned expressions are not safe to Eval.
   112  func DeserializeTableDescExpr(
   113  	ctx context.Context, semaCtx *tree.SemaContext, desc *sqlbase.TableDescriptor, exprStr string,
   114  ) (tree.Expr, error) {
   115  	expr, err := parser.ParseExpr(exprStr)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	expr, _, err = desc.ReplaceColumnVarsInExprWithDummies(expr)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	typed, err := expr.TypeCheck(ctx, semaCtx, types.Any)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return typed, nil
   128  }
   129  
   130  // FormatColumnForDisplay formats a column descriptor as a SQL string, and displays
   131  // user defined types in serialized expressions with human friendly formatting.
   132  func FormatColumnForDisplay(
   133  	ctx context.Context,
   134  	semaCtx *tree.SemaContext,
   135  	tbl *sqlbase.TableDescriptor,
   136  	desc *sqlbase.ColumnDescriptor,
   137  ) (string, error) {
   138  	f := tree.NewFmtCtx(tree.FmtSimple)
   139  	f.FormatNameP(&desc.Name)
   140  	f.WriteByte(' ')
   141  	f.WriteString(desc.Type.SQLString())
   142  	if desc.Nullable {
   143  		f.WriteString(" NULL")
   144  	} else {
   145  		f.WriteString(" NOT NULL")
   146  	}
   147  	if desc.DefaultExpr != nil {
   148  		f.WriteString(" DEFAULT ")
   149  		typed, err := DeserializeTableDescExpr(ctx, semaCtx, tbl, *desc.DefaultExpr)
   150  		if err != nil {
   151  			return "", err
   152  		}
   153  		f.WriteString(tree.SerializeForDisplay(typed))
   154  	}
   155  	if desc.IsComputed() {
   156  		f.WriteString(" AS (")
   157  		typed, err := DeserializeTableDescExpr(ctx, semaCtx, tbl, *desc.ComputeExpr)
   158  		if err != nil {
   159  			return "", err
   160  		}
   161  		f.WriteString(tree.SerializeForDisplay(typed))
   162  		f.WriteString(") STORED")
   163  	}
   164  	return f.CloseAndGetString(), nil
   165  }