github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/sem/tree/indexed_vars.go (about)

     1  // Copyright 2016 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/pgwire/pgcode"
    17  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgerror"
    18  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/types"
    19  	"github.com/cockroachdb/errors"
    20  	"github.com/cockroachdb/redact"
    21  )
    22  
    23  // IndexedVarContainer provides the implementation of TypeCheck, Eval, and
    24  // String for IndexedVars.
    25  type IndexedVarContainer interface {
    26  	IndexedVarResolvedType(idx int) *types.T
    27  	// IndexedVarNodeFormatter returns a NodeFormatter; if an object that
    28  	// wishes to implement this interface has lost the textual name that an
    29  	// IndexedVar originates from, this function can return nil (and the
    30  	// ordinal syntax "@1, @2, .." will be used).
    31  	IndexedVarNodeFormatter(idx int) NodeFormatter
    32  }
    33  
    34  // IndexedVar is a VariableExpr that can be used as a leaf in expressions; it
    35  // represents a dynamic value. It defers calls to TypeCheck, Eval, String to an
    36  // IndexedVarContainer.
    37  type IndexedVar struct {
    38  	Idx  int
    39  	Used bool
    40  
    41  	col NodeFormatter
    42  
    43  	typeAnnotation
    44  }
    45  
    46  var _ TypedExpr = &IndexedVar{}
    47  
    48  // Variable is a dummy function part of the VariableExpr interface.
    49  func (*IndexedVar) Variable() {}
    50  
    51  // Walk is part of the Expr interface.
    52  func (v *IndexedVar) Walk(_ Visitor) Expr {
    53  	return v
    54  }
    55  
    56  // TypeCheck is part of the Expr interface.
    57  func (v *IndexedVar) TypeCheck(
    58  	_ context.Context, semaCtx *SemaContext, desired *types.T,
    59  ) (TypedExpr, error) {
    60  	if semaCtx.IVarContainer == nil {
    61  		// A more technically correct message would be to say that the
    62  		// reference is unbound and thus cannot be typed. However this is
    63  		// a tad bit too technical for the average SQL use case and
    64  		// instead we acknowledge that we only get here if someone has
    65  		// used a column reference in a place where it's not allowed by
    66  		// the docs, so just say that instead.
    67  		return nil, pgerror.Newf(
    68  			pgcode.UndefinedColumn, "column reference @%d not allowed in this context", v.Idx+1)
    69  	}
    70  	v.typ = semaCtx.IVarContainer.IndexedVarResolvedType(v.Idx)
    71  	return v, nil
    72  }
    73  
    74  // ResolvedType is part of the TypedExpr interface.
    75  func (v *IndexedVar) ResolvedType() *types.T {
    76  	if v.typ == nil {
    77  		panic(errors.AssertionFailedf("indexed var must be type checked first"))
    78  	}
    79  	return v.typ
    80  }
    81  
    82  // Format implements the NodeFormatter interface.
    83  func (v *IndexedVar) Format(ctx *FmtCtx) {
    84  	f := ctx.flags
    85  	if ctx.indexedVarFormat != nil {
    86  		ctx.indexedVarFormat(ctx, v.Idx)
    87  	} else if f.HasFlags(fmtSymbolicVars) || v.col == nil {
    88  		ctx.Printf("@%d", v.Idx+1)
    89  	} else {
    90  		ctx.FormatNode(v.col)
    91  	}
    92  }
    93  
    94  // NewOrdinalReference is a helper routine to create a standalone
    95  // IndexedVar with the given index value. This needs to undergo
    96  // BindIfUnbound() below before it can be fully used.
    97  func NewOrdinalReference(r int) *IndexedVar {
    98  	return &IndexedVar{Idx: r}
    99  }
   100  
   101  // NewTypedOrdinalReference returns a new IndexedVar with the given index value
   102  // that is verified to be well-typed.
   103  func NewTypedOrdinalReference(r int, typ *types.T) *IndexedVar {
   104  	return &IndexedVar{Idx: r, typeAnnotation: typeAnnotation{typ: typ}}
   105  }
   106  
   107  // IndexedVarHelper wraps an IndexedVarContainer (an interface) and creates
   108  // IndexedVars bound to that container.
   109  //
   110  // It also keeps track of which indexes from the container are used by
   111  // expressions.
   112  type IndexedVarHelper struct {
   113  	vars      []IndexedVar
   114  	container IndexedVarContainer
   115  }
   116  
   117  // Container returns the container associated with the helper.
   118  func (h *IndexedVarHelper) Container() IndexedVarContainer {
   119  	return h.container
   120  }
   121  
   122  // BindIfUnbound ensures the IndexedVar is attached to this helper's container.
   123  // - for freshly created IndexedVars (with a nil container) this will bind in-place.
   124  // - for already bound IndexedVar, bound to this container, this will return the same ivar unchanged.
   125  // - for ordinal references (with an explicit unboundContainer) this will return a new var.
   126  // - for already bound IndexedVars, bound to another container, this will error out.
   127  func (h *IndexedVarHelper) BindIfUnbound(ivar *IndexedVar) (*IndexedVar, error) {
   128  	// We perform the range check always, even if the ivar is already
   129  	// bound, as a form of safety assertion against misreuse of ivars
   130  	// across containers.
   131  	if ivar.Idx < 0 || ivar.Idx >= len(h.vars) {
   132  		return ivar, pgerror.Newf(
   133  			pgcode.UndefinedColumn, "invalid column ordinal: @%d", ivar.Idx+1)
   134  	}
   135  
   136  	if !ivar.Used {
   137  		return h.IndexedVar(ivar.Idx), nil
   138  	}
   139  	return ivar, nil
   140  }
   141  
   142  // MakeIndexedVarHelper initializes an IndexedVarHelper structure.
   143  func MakeIndexedVarHelper(container IndexedVarContainer, numVars int) IndexedVarHelper {
   144  	return IndexedVarHelper{
   145  		vars:      make([]IndexedVar, numVars),
   146  		container: container,
   147  	}
   148  }
   149  
   150  // AppendSlot expands the capacity of this IndexedVarHelper by one and returns
   151  // the index of the new slot.
   152  func (h *IndexedVarHelper) AppendSlot() int {
   153  	h.vars = append(h.vars, IndexedVar{})
   154  	return len(h.vars) - 1
   155  }
   156  
   157  func (h *IndexedVarHelper) checkIndex(idx int) {
   158  	if idx < 0 || idx >= len(h.vars) {
   159  		panic(errors.AssertionFailedf(
   160  			"invalid var index %d (columns: %d)", redact.Safe(idx), redact.Safe(len(h.vars))))
   161  	}
   162  }
   163  
   164  // IndexedVar returns an IndexedVar for the given index. The index must be
   165  // valid.
   166  func (h *IndexedVarHelper) IndexedVar(idx int) *IndexedVar {
   167  	h.checkIndex(idx)
   168  	v := &h.vars[idx]
   169  	v.Idx = idx
   170  	v.Used = true
   171  	v.typ = h.container.IndexedVarResolvedType(idx)
   172  	v.col = h.container.IndexedVarNodeFormatter(idx)
   173  	return v
   174  }
   175  
   176  // IndexedVarWithType returns an IndexedVar for the given index, with the given
   177  // type. The index must be valid. This should be used in the case where an
   178  // indexed var is being added before its container has a corresponding entry
   179  // for it.
   180  func (h *IndexedVarHelper) IndexedVarWithType(idx int, typ *types.T) *IndexedVar {
   181  	h.checkIndex(idx)
   182  	v := &h.vars[idx]
   183  	v.Idx = idx
   184  	v.Used = true
   185  	v.typ = typ
   186  	return v
   187  }
   188  
   189  // IndexedVarUsed returns true if IndexedVar() was called for the given index.
   190  // The index must be valid.
   191  func (h *IndexedVarHelper) IndexedVarUsed(idx int) bool {
   192  	h.checkIndex(idx)
   193  	return h.vars[idx].Used
   194  }
   195  
   196  // GetIndexedVars returns the indexed var array of this helper.
   197  // IndexedVars to the caller; unused vars are guaranteed to have
   198  // a false Used field.
   199  func (h *IndexedVarHelper) GetIndexedVars() []IndexedVar {
   200  	return h.vars
   201  }
   202  
   203  // Rebind collects all the IndexedVars in the given expression and re-binds them
   204  // to this helper.
   205  func (h *IndexedVarHelper) Rebind(expr TypedExpr) TypedExpr {
   206  	if expr == nil {
   207  		return nil
   208  	}
   209  	ret, _ := WalkExpr(h, expr)
   210  	return ret.(TypedExpr)
   211  }
   212  
   213  var _ Visitor = &IndexedVarHelper{}
   214  
   215  // VisitPre implements the Visitor interface.
   216  func (h *IndexedVarHelper) VisitPre(expr Expr) (recurse bool, newExpr Expr) {
   217  	if iv, ok := expr.(*IndexedVar); ok {
   218  		return false, h.IndexedVar(iv.Idx)
   219  	}
   220  	return true, expr
   221  }
   222  
   223  // VisitPost implements the Visitor interface.
   224  func (*IndexedVarHelper) VisitPost(expr Expr) Expr { return expr }
   225  
   226  type typeContainer struct {
   227  	types []*types.T
   228  }
   229  
   230  var _ IndexedVarContainer = &typeContainer{}
   231  
   232  // IndexedVarResolvedType is part of the IndexedVarContainer interface.
   233  func (tc *typeContainer) IndexedVarResolvedType(idx int) *types.T {
   234  	return tc.types[idx]
   235  }
   236  
   237  // IndexedVarNodeFormatter is part of the IndexedVarContainer interface.
   238  func (tc *typeContainer) IndexedVarNodeFormatter(idx int) NodeFormatter {
   239  	return nil
   240  }
   241  
   242  // MakeTypesOnlyIndexedVarHelper creates an IndexedVarHelper which provides
   243  // the given types for indexed vars. It does not support evaluation, unless
   244  // Rebind is used with another container which supports evaluation.
   245  func MakeTypesOnlyIndexedVarHelper(types []*types.T) IndexedVarHelper {
   246  	c := &typeContainer{types: types}
   247  	return MakeIndexedVarHelper(c, len(types))
   248  }