github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/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/cockroach/pkg/sql/pgwire/pgcode"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    19  	"github.com/cockroachdb/cockroach/pkg/util/log"
    20  	"github.com/cockroachdb/errors"
    21  )
    22  
    23  // IndexedVarContainer provides the implementation of TypeCheck, Eval, and
    24  // String for IndexedVars.
    25  type IndexedVarContainer interface {
    26  	IndexedVarEval(idx int, ctx *EvalContext) (Datum, error)
    27  	IndexedVarResolvedType(idx int) *types.T
    28  	// IndexedVarNodeFormatter returns a NodeFormatter; if an object that
    29  	// wishes to implement this interface has lost the textual name that an
    30  	// IndexedVar originates from, this function can return nil (and the
    31  	// ordinal syntax "@1, @2, .." will be used).
    32  	IndexedVarNodeFormatter(idx int) NodeFormatter
    33  }
    34  
    35  // IndexedVar is a VariableExpr that can be used as a leaf in expressions; it
    36  // represents a dynamic value. It defers calls to TypeCheck, Eval, String to an
    37  // IndexedVarContainer.
    38  type IndexedVar struct {
    39  	Idx  int
    40  	Used bool
    41  
    42  	col NodeFormatter
    43  
    44  	typeAnnotation
    45  }
    46  
    47  var _ TypedExpr = &IndexedVar{}
    48  
    49  // Variable is a dummy function part of the VariableExpr interface.
    50  func (*IndexedVar) Variable() {}
    51  
    52  // Walk is part of the Expr interface.
    53  func (v *IndexedVar) Walk(_ Visitor) Expr {
    54  	return v
    55  }
    56  
    57  // TypeCheck is part of the Expr interface.
    58  func (v *IndexedVar) TypeCheck(
    59  	_ context.Context, semaCtx *SemaContext, desired *types.T,
    60  ) (TypedExpr, error) {
    61  	if semaCtx.IVarContainer == nil || semaCtx.IVarContainer == unboundContainer {
    62  		// A more technically correct message would be to say that the
    63  		// reference is unbound and thus cannot be typed. However this is
    64  		// a tad bit too technical for the average SQL use case and
    65  		// instead we acknowledge that we only get here if someone has
    66  		// used a column reference in a place where it's not allowed by
    67  		// the docs, so just say that instead.
    68  		return nil, pgerror.Newf(
    69  			pgcode.UndefinedColumn, "column reference @%d not allowed in this context", v.Idx+1)
    70  	}
    71  	v.typ = semaCtx.IVarContainer.IndexedVarResolvedType(v.Idx)
    72  	return v, nil
    73  }
    74  
    75  // Eval is part of the TypedExpr interface.
    76  func (v *IndexedVar) Eval(ctx *EvalContext) (Datum, error) {
    77  	if ctx.IVarContainer == nil || ctx.IVarContainer == unboundContainer {
    78  		return nil, errors.AssertionFailedf(
    79  			"indexed var must be bound to a container before evaluation")
    80  	}
    81  	return ctx.IVarContainer.IndexedVarEval(v.Idx, ctx)
    82  }
    83  
    84  // ResolvedType is part of the TypedExpr interface.
    85  func (v *IndexedVar) ResolvedType() *types.T {
    86  	if v.typ == nil {
    87  		panic(errors.AssertionFailedf("indexed var must be type checked first"))
    88  	}
    89  	return v.typ
    90  }
    91  
    92  // Format implements the NodeFormatter interface.
    93  func (v *IndexedVar) Format(ctx *FmtCtx) {
    94  	f := ctx.flags
    95  	if ctx.indexedVarFormat != nil {
    96  		ctx.indexedVarFormat(ctx, v.Idx)
    97  	} else if f.HasFlags(fmtSymbolicVars) || v.col == nil {
    98  		ctx.Printf("@%d", v.Idx+1)
    99  	} else {
   100  		v.col.Format(ctx)
   101  	}
   102  }
   103  
   104  // NewOrdinalReference is a helper routine to create a standalone
   105  // IndexedVar with the given index value. This needs to undergo
   106  // BindIfUnbound() below before it can be fully used.
   107  func NewOrdinalReference(r int) *IndexedVar {
   108  	return &IndexedVar{Idx: r}
   109  }
   110  
   111  // NewTypedOrdinalReference returns a new IndexedVar with the given index value
   112  // that is verified to be well-typed.
   113  func NewTypedOrdinalReference(r int, typ *types.T) *IndexedVar {
   114  	return &IndexedVar{Idx: r, typeAnnotation: typeAnnotation{typ: typ}}
   115  }
   116  
   117  // IndexedVarHelper wraps an IndexedVarContainer (an interface) and creates
   118  // IndexedVars bound to that container.
   119  //
   120  // It also keeps track of which indexes from the container are used by
   121  // expressions.
   122  type IndexedVarHelper struct {
   123  	vars      []IndexedVar
   124  	container IndexedVarContainer
   125  }
   126  
   127  // Container returns the container associated with the helper.
   128  func (h *IndexedVarHelper) Container() IndexedVarContainer {
   129  	return h.container
   130  }
   131  
   132  // BindIfUnbound ensures the IndexedVar is attached to this helper's container.
   133  // - for freshly created IndexedVars (with a nil container) this will bind in-place.
   134  // - for already bound IndexedVar, bound to this container, this will return the same ivar unchanged.
   135  // - for ordinal references (with an explicit unboundContainer) this will return a new var.
   136  // - for already bound IndexedVars, bound to another container, this will error out.
   137  func (h *IndexedVarHelper) BindIfUnbound(ivar *IndexedVar) (*IndexedVar, error) {
   138  	// We perform the range check always, even if the ivar is already
   139  	// bound, as a form of safety assertion against misreuse of ivars
   140  	// across containers.
   141  	if ivar.Idx < 0 || ivar.Idx >= len(h.vars) {
   142  		return ivar, pgerror.Newf(
   143  			pgcode.UndefinedColumn, "invalid column ordinal: @%d", ivar.Idx+1)
   144  	}
   145  
   146  	if !ivar.Used {
   147  		return h.IndexedVar(ivar.Idx), nil
   148  	}
   149  	return ivar, nil
   150  }
   151  
   152  // MakeIndexedVarHelper initializes an IndexedVarHelper structure.
   153  func MakeIndexedVarHelper(container IndexedVarContainer, numVars int) IndexedVarHelper {
   154  	return IndexedVarHelper{
   155  		vars:      make([]IndexedVar, numVars),
   156  		container: container,
   157  	}
   158  }
   159  
   160  // AppendSlot expands the capacity of this IndexedVarHelper by one and returns
   161  // the index of the new slot.
   162  func (h *IndexedVarHelper) AppendSlot() int {
   163  	h.vars = append(h.vars, IndexedVar{})
   164  	return len(h.vars) - 1
   165  }
   166  
   167  func (h *IndexedVarHelper) checkIndex(idx int) {
   168  	if idx < 0 || idx >= len(h.vars) {
   169  		panic(errors.AssertionFailedf(
   170  			"invalid var index %d (columns: %d)", log.Safe(idx), log.Safe(len(h.vars))))
   171  	}
   172  }
   173  
   174  // NumVars returns the number of variables the IndexedVarHelper was initialized
   175  // for.
   176  func (h *IndexedVarHelper) NumVars() int {
   177  	return len(h.vars)
   178  }
   179  
   180  // IndexedVar returns an IndexedVar for the given index. The index must be
   181  // valid.
   182  func (h *IndexedVarHelper) IndexedVar(idx int) *IndexedVar {
   183  	h.checkIndex(idx)
   184  	v := &h.vars[idx]
   185  	v.Idx = idx
   186  	v.Used = true
   187  	v.typ = h.container.IndexedVarResolvedType(idx)
   188  	v.col = h.container.IndexedVarNodeFormatter(idx)
   189  	return v
   190  }
   191  
   192  // IndexedVarWithType returns an IndexedVar for the given index, with the given
   193  // type. The index must be valid. This should be used in the case where an
   194  // indexed var is being added before its container has a corresponding entry
   195  // for it.
   196  func (h *IndexedVarHelper) IndexedVarWithType(idx int, typ *types.T) *IndexedVar {
   197  	h.checkIndex(idx)
   198  	v := &h.vars[idx]
   199  	v.Idx = idx
   200  	v.Used = true
   201  	v.typ = typ
   202  	return v
   203  }
   204  
   205  // IndexedVarUsed returns true if IndexedVar() was called for the given index.
   206  // The index must be valid.
   207  func (h *IndexedVarHelper) IndexedVarUsed(idx int) bool {
   208  	h.checkIndex(idx)
   209  	return h.vars[idx].Used
   210  }
   211  
   212  // GetIndexedVars returns the indexed var array of this helper.
   213  // IndexedVars to the caller; unused vars are guaranteed to have
   214  // a false Used field.
   215  func (h *IndexedVarHelper) GetIndexedVars() []IndexedVar {
   216  	return h.vars
   217  }
   218  
   219  // Reset re-initializes an IndexedVarHelper structure with the same
   220  // number of slots. After a helper has been reset, all the expressions
   221  // that were linked to the helper before it was reset must be
   222  // re-bound, e.g. using Rebind(). Resetting is useful to ensure that
   223  // the helper's knowledge of which IndexedVars are actually used by
   224  // linked expressions is up to date, especially after
   225  // optimizations/transforms which eliminate sub-expressions. The
   226  // optimizations performed by setNeededColumns() work then best.
   227  //
   228  // TODO(knz): groupNode and windowNode hold on to IndexedVar's after a Reset().
   229  func (h *IndexedVarHelper) Reset() {
   230  	h.vars = make([]IndexedVar, len(h.vars))
   231  }
   232  
   233  // Rebind collects all the IndexedVars in the given expression
   234  // and re-binds them to this helper.
   235  func (h *IndexedVarHelper) Rebind(expr TypedExpr, alsoReset, normalizeToNonNil bool) TypedExpr {
   236  	if alsoReset {
   237  		h.Reset()
   238  	}
   239  	if expr == nil || expr == DBoolTrue {
   240  		if normalizeToNonNil {
   241  			return DBoolTrue
   242  		}
   243  		return nil
   244  	}
   245  	ret, _ := WalkExpr(h, expr)
   246  	return ret.(TypedExpr)
   247  }
   248  
   249  var _ Visitor = &IndexedVarHelper{}
   250  
   251  // VisitPre implements the Visitor interface.
   252  func (h *IndexedVarHelper) VisitPre(expr Expr) (recurse bool, newExpr Expr) {
   253  	if iv, ok := expr.(*IndexedVar); ok {
   254  		return false, h.IndexedVar(iv.Idx)
   255  	}
   256  	return true, expr
   257  }
   258  
   259  // VisitPost implements the Visitor interface.
   260  func (*IndexedVarHelper) VisitPost(expr Expr) Expr { return expr }
   261  
   262  type unboundContainerType struct{}
   263  
   264  // unboundContainer is the marker used by ordinal references (@N) in
   265  // the input syntax. It differs from `nil` in that calling
   266  // BindIfUnbound on an indexed var that uses unboundContainer will
   267  // cause a new IndexedVar object to be returned, to ensure that the
   268  // original is left unchanged (to preserve the invariant that the AST
   269  // is constant after parse).
   270  var unboundContainer = &unboundContainerType{}
   271  
   272  // IndexedVarEval is part of the IndexedVarContainer interface.
   273  func (*unboundContainerType) IndexedVarEval(idx int, _ *EvalContext) (Datum, error) {
   274  	return nil, errors.AssertionFailedf("unbound ordinal reference @%d", log.Safe(idx+1))
   275  }
   276  
   277  // IndexedVarResolvedType is part of the IndexedVarContainer interface.
   278  func (*unboundContainerType) IndexedVarResolvedType(idx int) *types.T {
   279  	panic(errors.AssertionFailedf("unbound ordinal reference @%d", log.Safe(idx+1)))
   280  }
   281  
   282  // IndexedVarNodeFormatter is part of the IndexedVarContainer interface.
   283  func (*unboundContainerType) IndexedVarNodeFormatter(idx int) NodeFormatter {
   284  	panic(errors.AssertionFailedf("unbound ordinal reference @%d", log.Safe(idx+1)))
   285  }
   286  
   287  type typeContainer struct {
   288  	types []*types.T
   289  }
   290  
   291  var _ IndexedVarContainer = &typeContainer{}
   292  
   293  // IndexedVarEval is part of the IndexedVarContainer interface.
   294  func (tc *typeContainer) IndexedVarEval(idx int, ctx *EvalContext) (Datum, error) {
   295  	return nil, errors.AssertionFailedf("no eval allowed in typeContainer")
   296  }
   297  
   298  // IndexedVarResolvedType is part of the IndexedVarContainer interface.
   299  func (tc *typeContainer) IndexedVarResolvedType(idx int) *types.T {
   300  	return tc.types[idx]
   301  }
   302  
   303  // IndexedVarNodeFormatter is part of the IndexedVarContainer interface.
   304  func (tc *typeContainer) IndexedVarNodeFormatter(idx int) NodeFormatter {
   305  	return nil
   306  }
   307  
   308  // MakeTypesOnlyIndexedVarHelper creates an IndexedVarHelper which provides
   309  // the given types for indexed vars. It does not support evaluation, unless
   310  // Rebind is used with another container which supports evaluation.
   311  func MakeTypesOnlyIndexedVarHelper(types []*types.T) IndexedVarHelper {
   312  	c := &typeContainer{types: types}
   313  	return MakeIndexedVarHelper(c, len(types))
   314  }