github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/schema/column.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package schema
    16  
    17  import (
    18  	"errors"
    19  	"math"
    20  	"strings"
    21  
    22  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    23  	"github.com/dolthub/dolt/go/store/types"
    24  )
    25  
    26  // InvalidTag is used as an invalid tag
    27  const InvalidTag uint64 = math.MaxUint64
    28  
    29  var (
    30  	// KindToLwrStr maps a noms kind to the kinds lowercased name
    31  	KindToLwrStr = make(map[types.NomsKind]string)
    32  
    33  	// LwrStrToKind maps a lowercase string to the noms kind it is referring to
    34  	LwrStrToKind = make(map[string]types.NomsKind)
    35  )
    36  
    37  var (
    38  	// InvalidCol is a Column instance that is returned when there is nothing to return and can be tested against.
    39  	InvalidCol = Column{
    40  		Name:     "invalid",
    41  		Tag:      InvalidTag,
    42  		Kind:     types.NullKind,
    43  		TypeInfo: typeinfo.UnknownType,
    44  	}
    45  )
    46  
    47  func init() {
    48  	for t, s := range types.KindToString {
    49  		KindToLwrStr[t] = strings.ToLower(s)
    50  		LwrStrToKind[strings.ToLower(s)] = t
    51  	}
    52  }
    53  
    54  // Column is a structure containing information about a column in a row in a table.
    55  type Column struct {
    56  	// Name is the name of the column
    57  	Name string
    58  
    59  	// Tag should be unique per versioned schema and allows
    60  	Tag uint64
    61  
    62  	// Kind is the types.NomsKind that values of this column will be
    63  	Kind types.NomsKind
    64  
    65  	// IsPartOfPK says whether this column is part of the primary key
    66  	IsPartOfPK bool
    67  
    68  	// TypeInfo states the type of this column.
    69  	TypeInfo typeinfo.TypeInfo
    70  
    71  	// Default is the default value of this column. This is the string representation of a sql.Expression.
    72  	Default string
    73  
    74  	// Generated is the generated value of this column. This is the string representation of a sql.Expression.
    75  	Generated string
    76  
    77  	// OnUpdate is the on update value of this column. This is the string representation of a sql.Expression.
    78  	OnUpdate string
    79  
    80  	// Virtual is true if this is a virtual column.
    81  	Virtual bool
    82  
    83  	// AutoIncrement says whether this column auto increments.
    84  	AutoIncrement bool
    85  
    86  	// Comment is the comment for this column.
    87  	Comment string
    88  
    89  	// Constraints are rules that can be checked on each column to say if the columns value is valid
    90  	Constraints []ColConstraint
    91  }
    92  
    93  // NewColumn creates a Column instance with the default type info for the NomsKind
    94  func NewColumn(name string, tag uint64, kind types.NomsKind, partOfPK bool, constraints ...ColConstraint) Column {
    95  	typeInfo := typeinfo.FromKind(kind)
    96  	col, err := NewColumnWithTypeInfo(name, tag, typeInfo, partOfPK, "", false, "", constraints...)
    97  	if err != nil {
    98  		panic(err)
    99  	}
   100  	return col
   101  }
   102  
   103  // NewColumnWithTypeInfo creates a Column instance with the given type info.
   104  // Callers are encouraged to construct schema.Column structs directly instead of using this method, then call
   105  // ValidateColumn.
   106  func NewColumnWithTypeInfo(name string, tag uint64, typeInfo typeinfo.TypeInfo, partOfPK bool, defaultVal string, autoIncrement bool, comment string, constraints ...ColConstraint) (Column, error) {
   107  	c := Column{
   108  		Name:          name,
   109  		Tag:           tag,
   110  		Kind:          typeInfo.NomsKind(),
   111  		IsPartOfPK:    partOfPK,
   112  		TypeInfo:      typeInfo,
   113  		Default:       defaultVal,
   114  		AutoIncrement: autoIncrement,
   115  		Comment:       comment,
   116  		Constraints:   constraints,
   117  	}
   118  
   119  	err := ValidateColumn(c)
   120  	if err != nil {
   121  		return InvalidCol, err
   122  	}
   123  
   124  	return c, nil
   125  }
   126  
   127  // ValidateColumn validates the given column.
   128  func ValidateColumn(c Column) error {
   129  	for _, c := range c.Constraints {
   130  		if c == nil {
   131  			return errors.New("nil passed as a constraint")
   132  		}
   133  	}
   134  
   135  	if c.TypeInfo == nil {
   136  		return errors.New("cannot instantiate column with nil type info")
   137  	}
   138  
   139  	if c.TypeInfo.NomsKind() != c.Kind {
   140  		return errors.New("type info and kind do not match")
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // IsNullable returns whether the column can be set to a null value.
   147  func (c Column) IsNullable() bool {
   148  	if c.IsPartOfPK {
   149  		return false
   150  	}
   151  	if c.AutoIncrement {
   152  		return false
   153  	}
   154  	for _, cnst := range c.Constraints {
   155  		if cnst.GetConstraintType() == NotNullConstraintType {
   156  			return false
   157  		}
   158  	}
   159  	return true
   160  }
   161  
   162  // Equals tests equality between two columns.
   163  func (c Column) Equals(other Column) bool {
   164  	return c.Name == other.Name &&
   165  		c.Tag == other.Tag &&
   166  		c.Kind == other.Kind &&
   167  		c.IsPartOfPK == other.IsPartOfPK &&
   168  		c.TypeInfo.Equals(other.TypeInfo) &&
   169  		c.Default == other.Default &&
   170  		ColConstraintsAreEqual(c.Constraints, other.Constraints)
   171  }
   172  
   173  // EqualsWithoutTag tests equality between two columns, but does not check the columns' tags.
   174  func (c Column) EqualsWithoutTag(other Column) bool {
   175  	return c.Name == other.Name &&
   176  		c.Kind == other.Kind &&
   177  		c.IsPartOfPK == other.IsPartOfPK &&
   178  		c.TypeInfo.Equals(other.TypeInfo) &&
   179  		c.Default == other.Default &&
   180  		ColConstraintsAreEqual(c.Constraints, other.Constraints)
   181  }
   182  
   183  // Compatible tests compatibility between two columns. Compatible columns have the same tag and can store the same
   184  // kinds of values at the storage layer, but may have different constraints or type parameters.
   185  func (c Column) Compatible(other Column) bool {
   186  	return c.Tag == other.Tag &&
   187  		c.Kind == other.Kind &&
   188  		c.IsPartOfPK == other.IsPartOfPK
   189  }
   190  
   191  // KindString returns the string representation of the NomsKind stored in the column.
   192  func (c Column) KindString() string {
   193  	return KindToLwrStr[c.Kind]
   194  }