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

     1  // Copyright 2022 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 cast defines the semantically allowed casts and their information.
    12  //
    13  // Note that it does not provide the mechanism to perform the evaluation of
    14  // these casts.
    15  package cast
    16  
    17  import (
    18  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/sem/volatility"
    19  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/types"
    20  	"github.com/lib/pq/oid"
    21  )
    22  
    23  // Context represents the contexts in which a cast can be performed. There
    24  // are three types of cast contexts: explicit, assignment, and implicit. Not all
    25  // casts can be performed in all contexts. See the description of each context
    26  // below for more details.
    27  //
    28  // The concept of cast contexts is taken directly from Postgres's cast behavior.
    29  // More information can be found in the Postgres documentation on type
    30  // conversion: https://www.postgresql.org/docs/current/typeconv.html
    31  type Context uint8
    32  
    33  const (
    34  	_ Context = iota
    35  	// ContextExplicit is a cast performed explicitly with the syntax
    36  	// CAST(x AS T) or x::T.
    37  	ContextExplicit
    38  	// ContextAssignment is a cast implicitly performed during an INSERT,
    39  	// UPSERT, or UPDATE statement.
    40  	ContextAssignment
    41  	// ContextImplicit is a cast performed implicitly. For example, the DATE
    42  	// below is implicitly cast to a TIMESTAMPTZ so that the values can be
    43  	// compared.
    44  	//
    45  	//   SELECT '2021-01-10'::DATE < now()
    46  	//
    47  	ContextImplicit
    48  )
    49  
    50  // String returns the representation of Context as a string.
    51  func (cc Context) String() string {
    52  	switch cc {
    53  	case ContextExplicit:
    54  		return "explicit"
    55  	case ContextAssignment:
    56  		return "assignment"
    57  	case ContextImplicit:
    58  		return "implicit"
    59  	default:
    60  		return "invalid"
    61  	}
    62  }
    63  
    64  // PGString returns the representation of Context as an abbreviated string.
    65  func (cc Context) PGString() string {
    66  	switch cc {
    67  	case ContextExplicit:
    68  		return "e"
    69  	case ContextAssignment:
    70  		return "a"
    71  	case ContextImplicit:
    72  		return "i"
    73  	}
    74  	return ""
    75  }
    76  
    77  // ContextOrigin indicates the source of information for a cast's maximum
    78  // context (see cast.MaxContext below). It is only used to annotate entries in
    79  // castMap and to perform assertions on cast entries in the init function. It
    80  // has no effect on the behavior of a cast.
    81  type ContextOrigin uint8
    82  
    83  const (
    84  	_ ContextOrigin = iota
    85  	// ContextOriginPgCast specifies that a cast's maximum context is based on
    86  	// information in Postgres's pg_cast table.
    87  	ContextOriginPgCast
    88  	// ContextOriginAutomaticIOConversion specifies that a cast's maximum
    89  	// context is not included in Postgres's pg_cast table. In Postgres's
    90  	// internals, these casts are evaluated by each data type's input and output
    91  	// functions.
    92  	//
    93  	// Automatic casts can only convert to or from string types [1]. Conversions
    94  	// to string types are assignment casts and conversions from string types
    95  	// are explicit casts [2]. These rules are asserted in the init function.
    96  	//
    97  	// [1] https://www.postgresql.org/docs/13/catalog-pg-cast.html#CATALOG-PG-CAST
    98  	// [2] https://www.postgresql.org/docs/13/sql-createcast.html#SQL-CREATECAST-NOTES
    99  	ContextOriginAutomaticIOConversion
   100  	// ContextOriginLegacyConversion is used for casts that are not supported by
   101  	// Postgres, but are supported by CockroachDB and continue to be supported
   102  	// for backwards compatibility.
   103  	ContextOriginLegacyConversion
   104  )
   105  
   106  // Cast includes details about a cast from one OID to another.
   107  //
   108  // TODO(mgartner, otan): Move PerformCast logic to this struct.
   109  type Cast struct {
   110  	// MaxContext is the maximum context in which the cast is allowed. A cast
   111  	// can only be performed in a context that is at or below the specified
   112  	// maximum context.
   113  	//
   114  	// ContextExplicit casts can only be performed in an explicit context.
   115  	//
   116  	// ContextAssignment casts can be performed in an explicit context or in
   117  	// an assignment context in an INSERT, UPSERT, or UPDATE statement.
   118  	//
   119  	// ContextImplicit casts can be performed in any context.
   120  	MaxContext Context
   121  	// origin is the source of truth for the cast's context. It is used to
   122  	// annotate entries in castMap and to perform assertions on cast entries in
   123  	// the init function. It has no effect on the behavior of a cast.
   124  	origin ContextOrigin
   125  	// Volatility indicates whether the result of the cast is dependent only on
   126  	// the source value, or dependent on outside factors (such as parameter
   127  	// variables or table contents).
   128  	Volatility volatility.V
   129  	// VolatilityHint is an optional string for volatility.Stable casts. When
   130  	// set, it is used as an error hint suggesting a possible workaround when
   131  	// stable casts are not allowed.
   132  	VolatilityHint string
   133  }
   134  
   135  // ForEachCast calls fn for every valid cast from a source type to a target
   136  // type.
   137  func ForEachCast(
   138  	fn func(
   139  		src oid.Oid, tgt oid.Oid, castCtx Context, ctxOrigin ContextOrigin, v volatility.V,
   140  	),
   141  ) {
   142  	for src, tgts := range castMap {
   143  		for tgt, cast := range tgts {
   144  			fn(src, tgt, cast.MaxContext, cast.origin, cast.Volatility)
   145  		}
   146  	}
   147  }
   148  
   149  // ValidCast returns true if a valid cast exists from src to tgt in the given
   150  // context.
   151  func ValidCast(src, tgt *types.T, ctx Context) bool {
   152  	srcFamily := src.Family()
   153  	tgtFamily := tgt.Family()
   154  
   155  	// If src and tgt are array types, check for a valid cast between their
   156  	// content types.
   157  	if srcFamily == types.ArrayFamily && tgtFamily == types.ArrayFamily {
   158  		return ValidCast(src.ArrayContents(), tgt.ArrayContents(), ctx)
   159  	}
   160  
   161  	// If src and tgt are tuple types, check for a valid cast between each
   162  	// corresponding tuple element.
   163  	//
   164  	// Casts from a tuple type to AnyTuple are a no-op so they are always valid.
   165  	// If tgt is AnyTuple, we continue to LookupCast below which contains a
   166  	// special case for these casts.
   167  	if srcFamily == types.TupleFamily && tgtFamily == types.TupleFamily && tgt != types.AnyTuple {
   168  		srcTypes := src.TupleContents()
   169  		tgtTypes := tgt.TupleContents()
   170  		// The tuple types must have the same number of elements.
   171  		if len(srcTypes) != len(tgtTypes) {
   172  			return false
   173  		}
   174  		for i := range srcTypes {
   175  			if ok := ValidCast(srcTypes[i], tgtTypes[i], ctx); !ok {
   176  				return false
   177  			}
   178  		}
   179  		return true
   180  	}
   181  
   182  	// If src and tgt are not both array or tuple types, check castMap for a
   183  	// valid cast.
   184  	c, ok := LookupCast(src, tgt)
   185  	if ok {
   186  		return c.MaxContext >= ctx
   187  	}
   188  
   189  	return false
   190  }
   191  
   192  // OIDInCastMap checks to see if the cast is in the cast map. This bypasses
   193  // a few false equivalences found in LookupCast.
   194  // You are more likely using to use LookupCast.
   195  func OIDInCastMap(src, tgt oid.Oid) bool {
   196  	_, ok := castMap[src][tgt]
   197  	return ok
   198  }
   199  
   200  // LookupCast returns a cast that describes the cast from src to tgt if it
   201  // exists. If it does not exist, ok=false is returned.
   202  func LookupCast(src, tgt *types.T) (Cast, bool) {
   203  	srcFamily := src.Family()
   204  	tgtFamily := tgt.Family()
   205  
   206  	// Unknown is the type given to an expression that statically evaluates
   207  	// to NULL. NULL can be immutably cast to any type in any context.
   208  	if srcFamily == types.UnknownFamily {
   209  		return Cast{
   210  			MaxContext: ContextImplicit,
   211  			Volatility: volatility.Immutable,
   212  		}, true
   213  	}
   214  
   215  	// Enums have dynamic OIDs, so they can't be populated in castMap. Instead,
   216  	// we dynamically create cast structs for valid enum casts.
   217  	if srcFamily == types.EnumFamily && tgtFamily == types.StringFamily {
   218  		// Casts from enum types to strings are immutable and allowed in
   219  		// assignment contexts.
   220  		return Cast{
   221  			MaxContext: ContextAssignment,
   222  			Volatility: volatility.Immutable,
   223  		}, true
   224  	}
   225  	if tgtFamily == types.EnumFamily {
   226  		switch srcFamily {
   227  		case types.StringFamily:
   228  			// Casts from string types to enums are immutable and allowed in
   229  			// explicit contexts.
   230  			return Cast{
   231  				MaxContext: ContextExplicit,
   232  				Volatility: volatility.Immutable,
   233  			}, true
   234  		case types.UnknownFamily:
   235  			// Casts from unknown to enums are immutable and allowed in implicit
   236  			// contexts.
   237  			return Cast{
   238  				MaxContext: ContextImplicit,
   239  				Volatility: volatility.Immutable,
   240  			}, true
   241  		}
   242  	}
   243  
   244  	// Casts from array types to string types are stable and allowed in
   245  	// assignment contexts.
   246  	if srcFamily == types.ArrayFamily && tgtFamily == types.StringFamily {
   247  		return Cast{
   248  			MaxContext: ContextAssignment,
   249  			Volatility: volatility.Stable,
   250  		}, true
   251  	}
   252  
   253  	// Casts from array and tuple types to string types are immutable and
   254  	// allowed in assignment contexts.
   255  	// TODO(mgartner): Tuple to string casts should be stable. They are
   256  	// immutable to avoid backward incompatibility with previous versions, but
   257  	// this is incorrect and can causes corrupt indexes, corrupt tables, and
   258  	// incorrect query results.
   259  	if srcFamily == types.TupleFamily && tgtFamily == types.StringFamily {
   260  		return Cast{
   261  			MaxContext: ContextAssignment,
   262  			Volatility: volatility.Immutable,
   263  		}, true
   264  	}
   265  
   266  	// Casts from any tuple type to AnyTuple are no-ops, so they are implicit
   267  	// and immutable.
   268  	if srcFamily == types.TupleFamily && tgt == types.AnyTuple {
   269  		return Cast{
   270  			MaxContext: ContextImplicit,
   271  			Volatility: volatility.Immutable,
   272  		}, true
   273  	}
   274  
   275  	// Casts from string types to array and tuple types are stable and allowed
   276  	// in explicit contexts.
   277  	if srcFamily == types.StringFamily &&
   278  		(tgtFamily == types.ArrayFamily || tgtFamily == types.TupleFamily) {
   279  		return Cast{
   280  			MaxContext: ContextExplicit,
   281  			Volatility: volatility.Stable,
   282  		}, true
   283  	}
   284  
   285  	// Casts from int types to bit and varbit types are allowed only if the the
   286  	// length of the bit or varbit is defined
   287  	if srcFamily == types.IntFamily &&
   288  		tgtFamily == types.BitFamily &&
   289  		tgt.Width() != 0 {
   290  		return Cast{
   291  			MaxContext: ContextExplicit,
   292  			Volatility: volatility.Immutable,
   293  		}, true
   294  	}
   295  
   296  	if tgts, ok := castMap[src.Oid()]; ok {
   297  		if c, ok := tgts[tgt.Oid()]; ok {
   298  			return c, true
   299  		}
   300  	}
   301  
   302  	// If src and tgt are the same type, the immutable cast is valid in any
   303  	// context. This logic is intentionally after the lookup into castMap so
   304  	// that entries in castMap are preferred.
   305  	if src.Oid() == tgt.Oid() {
   306  		return Cast{
   307  			MaxContext: ContextImplicit,
   308  			Volatility: volatility.Immutable,
   309  		}, true
   310  	}
   311  
   312  	return Cast{}, false
   313  }
   314  
   315  // LookupCastVolatility returns the Volatility of a valid cast.
   316  func LookupCastVolatility(from, to *types.T) (_ volatility.V, ok bool) {
   317  	fromFamily := from.Family()
   318  	toFamily := to.Family()
   319  	// Special case for casting between arrays.
   320  	if fromFamily == types.ArrayFamily && toFamily == types.ArrayFamily {
   321  		return LookupCastVolatility(from.ArrayContents(), to.ArrayContents())
   322  	}
   323  	// Special case for casting between tuples.
   324  	if fromFamily == types.TupleFamily && toFamily == types.TupleFamily {
   325  		fromTypes := from.TupleContents()
   326  		toTypes := to.TupleContents()
   327  		// Handle case where an overload makes a tuple get casted to tuple{}.
   328  		if len(toTypes) == 1 && toTypes[0].Family() == types.AnyFamily {
   329  			return volatility.Stable, true
   330  		}
   331  		if len(fromTypes) != len(toTypes) {
   332  			return 0, false
   333  		}
   334  		maxVolatility := volatility.Leakproof
   335  		for i := range fromTypes {
   336  			v, lookupOk := LookupCastVolatility(fromTypes[i], toTypes[i])
   337  			if !lookupOk {
   338  				return 0, false
   339  			}
   340  			if v > maxVolatility {
   341  				maxVolatility = v
   342  			}
   343  		}
   344  		return maxVolatility, true
   345  	}
   346  
   347  	cast, ok := LookupCast(from, to)
   348  	if !ok {
   349  		return 0, false
   350  	}
   351  	return cast.Volatility, true
   352  }