vitess.io/vitess@v0.16.2/go/vt/vttablet/onlineddl/vrepl/types.go (about)

     1  /*
     2  	Original copyright by GitHub as follows. Additions by the Vitess authors as follows.
     3  */
     4  /*
     5     Copyright 2016 GitHub Inc.
     6  	 See https://github.com/github/gh-ost/blob/master/LICENSE
     7  */
     8  /*
     9  Copyright 2021 The Vitess Authors.
    10  
    11  Licensed under the Apache License, Version 2.0 (the "License");
    12  you may not use this file except in compliance with the License.
    13  You may obtain a copy of the License at
    14  
    15      http://www.apache.org/licenses/LICENSE-2.0
    16  
    17  Unless required by applicable law or agreed to in writing, software
    18  distributed under the License is distributed on an "AS IS" BASIS,
    19  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    20  See the License for the specific language governing permissions and
    21  limitations under the License.
    22  */
    23  
    24  package vrepl
    25  
    26  import (
    27  	"fmt"
    28  	"reflect"
    29  	"strings"
    30  
    31  	"vitess.io/vitess/go/vt/schemadiff"
    32  )
    33  
    34  // ColumnType indicated some MySQL data types
    35  type ColumnType int
    36  
    37  const (
    38  	UnknownColumnType ColumnType = iota
    39  	TimestampColumnType
    40  	DateTimeColumnType
    41  	EnumColumnType
    42  	SetColumnType
    43  	MediumIntColumnType
    44  	JSONColumnType
    45  	FloatColumnType
    46  	DoubleColumnType
    47  	BinaryColumnType
    48  	StringColumnType
    49  	IntegerColumnType
    50  )
    51  
    52  // Column represents a table column
    53  type Column struct {
    54  	Name                 string
    55  	IsUnsigned           bool
    56  	Charset              string
    57  	Collation            string
    58  	Type                 ColumnType
    59  	EnumValues           string
    60  	EnumToTextConversion bool
    61  	DataType             string // from COLUMN_TYPE column
    62  
    63  	IsNullable    bool
    64  	IsDefaultNull bool
    65  
    66  	CharacterMaximumLength int64
    67  	NumericPrecision       int64
    68  	NumericScale           int64
    69  	DateTimePrecision      int64
    70  
    71  	// add Octet length for binary type, fix bytes with suffix "00" get clipped in mysql binlog.
    72  	// https://github.com/github/gh-ost/issues/909
    73  	BinaryOctetLength uint64
    74  }
    75  
    76  // SetTypeIfUnknown will set a new column type only if the current type is unknown, otherwise silently skip
    77  func (c *Column) SetTypeIfUnknown(t ColumnType) {
    78  	if c.Type == UnknownColumnType {
    79  		c.Type = t
    80  	}
    81  }
    82  
    83  // HasDefault returns true if the column at all has a default value (possibly NULL)
    84  func (c *Column) HasDefault() bool {
    85  	if c.IsDefaultNull && !c.IsNullable {
    86  		// based on INFORMATION_SCHEMA.COLUMNS, this is the indicator for a 'NOT NULL' column with no default value.
    87  		return false
    88  	}
    89  	return true
    90  }
    91  
    92  // IsNumeric returns true if the column is of a numeric type
    93  func (c *Column) IsNumeric() bool {
    94  	return c.NumericPrecision > 0
    95  }
    96  
    97  // IsIntegralType returns true if the column is some form of an integer
    98  func (c *Column) IsIntegralType() bool {
    99  	return schemadiff.IsIntegralType(c.DataType)
   100  }
   101  
   102  // IsFloatingPoint returns true if the column is of a floating point numeric type
   103  func (c *Column) IsFloatingPoint() bool {
   104  	return c.Type == FloatColumnType || c.Type == DoubleColumnType
   105  }
   106  
   107  // IsFloatingPoint returns true if the column is of a temporal type
   108  func (c *Column) IsTemporal() bool {
   109  	return c.DateTimePrecision >= 0
   110  }
   111  
   112  // NewColumns creates a new column array from non empty names
   113  func NewColumns(names []string) []Column {
   114  	result := []Column{}
   115  	for _, name := range names {
   116  		if name == "" {
   117  			continue
   118  		}
   119  		result = append(result, Column{Name: name})
   120  	}
   121  	return result
   122  }
   123  
   124  // ParseColumns creates a new column array fby parsing comma delimited names list
   125  func ParseColumns(names string) []Column {
   126  	namesArray := strings.Split(names, ",")
   127  	return NewColumns(namesArray)
   128  }
   129  
   130  // ColumnsMap maps a column name onto its ordinal position
   131  type ColumnsMap map[string]int
   132  
   133  // NewEmptyColumnsMap creates an empty map
   134  func NewEmptyColumnsMap() ColumnsMap {
   135  	columnsMap := make(map[string]int)
   136  	return ColumnsMap(columnsMap)
   137  }
   138  
   139  // NewColumnsMap creates a column map based on ordered list of columns
   140  func NewColumnsMap(orderedColumns []Column) ColumnsMap {
   141  	columnsMap := NewEmptyColumnsMap()
   142  	for i, column := range orderedColumns {
   143  		columnsMap[column.Name] = i
   144  	}
   145  	return columnsMap
   146  }
   147  
   148  // ColumnList makes for a named list of columns
   149  type ColumnList struct {
   150  	columns  []Column
   151  	Ordinals ColumnsMap
   152  }
   153  
   154  // NewColumnList creates an object given ordered list of column names
   155  func NewColumnList(names []string) *ColumnList {
   156  	result := &ColumnList{
   157  		columns: NewColumns(names),
   158  	}
   159  	result.Ordinals = NewColumnsMap(result.columns)
   160  	return result
   161  }
   162  
   163  // ParseColumnList parses a comma delimited list of column names
   164  func ParseColumnList(names string) *ColumnList {
   165  	result := &ColumnList{
   166  		columns: ParseColumns(names),
   167  	}
   168  	result.Ordinals = NewColumnsMap(result.columns)
   169  	return result
   170  }
   171  
   172  // Columns returns the list of columns
   173  func (l *ColumnList) Columns() []Column {
   174  	return l.columns
   175  }
   176  
   177  // Names returns list of column names
   178  func (l *ColumnList) Names() []string {
   179  	names := make([]string, len(l.columns))
   180  	for i := range l.columns {
   181  		names[i] = l.columns[i].Name
   182  	}
   183  	return names
   184  }
   185  
   186  // GetColumn gets a column by name
   187  func (l *ColumnList) GetColumn(columnName string) *Column {
   188  	if ordinal, ok := l.Ordinals[columnName]; ok {
   189  		return &l.columns[ordinal]
   190  	}
   191  	return nil
   192  }
   193  
   194  // ColumnExists returns true if this column list has a column by a given name
   195  func (l *ColumnList) ColumnExists(columnName string) bool {
   196  	_, ok := l.Ordinals[columnName]
   197  	return ok
   198  }
   199  
   200  // String returns a comma separated list of column names
   201  func (l *ColumnList) String() string {
   202  	return strings.Join(l.Names(), ",")
   203  }
   204  
   205  // Equals checks for complete (deep) identities of columns, in order.
   206  func (l *ColumnList) Equals(other *ColumnList) bool {
   207  	return reflect.DeepEqual(l.Columns, other.Columns)
   208  }
   209  
   210  // EqualsByNames chcks if the names in this list equals the names of another list, in order. Type is ignored.
   211  func (l *ColumnList) EqualsByNames(other *ColumnList) bool {
   212  	return reflect.DeepEqual(l.Names(), other.Names())
   213  }
   214  
   215  // IsSubsetOf returns 'true' when column names of this list are a subset of
   216  // another list, in arbitrary order (order agnostic)
   217  func (l *ColumnList) IsSubsetOf(other *ColumnList) bool {
   218  	for _, column := range l.columns {
   219  		if _, exists := other.Ordinals[column.Name]; !exists {
   220  			return false
   221  		}
   222  	}
   223  	return true
   224  }
   225  
   226  // Difference returns a (new copy) subset of this column list, consisting of all
   227  // column NOT in given list.
   228  // The result is never nil, even if the difference is empty
   229  func (l *ColumnList) Difference(other *ColumnList) (diff *ColumnList) {
   230  	names := []string{}
   231  	for _, column := range l.columns {
   232  		if !other.ColumnExists(column.Name) {
   233  			names = append(names, column.Name)
   234  		}
   235  	}
   236  	return NewColumnList(names)
   237  }
   238  
   239  // Len returns the length of this list
   240  func (l *ColumnList) Len() int {
   241  	return len(l.columns)
   242  }
   243  
   244  // MappedNamesColumnList returns a column list based on this list, with names possibly mapped by given map
   245  func (l *ColumnList) MappedNamesColumnList(columnNamesMap map[string]string) *ColumnList {
   246  	names := l.Names()
   247  	for i := range names {
   248  		if mappedName, ok := columnNamesMap[names[i]]; ok {
   249  			names[i] = mappedName
   250  		}
   251  	}
   252  	return NewColumnList(names)
   253  }
   254  
   255  // SetEnumToTextConversion tells this column list that an enum is conveted to text
   256  func (l *ColumnList) SetEnumToTextConversion(columnName string, enumValues string) {
   257  	l.GetColumn(columnName).EnumToTextConversion = true
   258  	l.GetColumn(columnName).EnumValues = enumValues
   259  }
   260  
   261  // IsEnumToTextConversion tells whether an enum was converted to text
   262  func (l *ColumnList) IsEnumToTextConversion(columnName string) bool {
   263  	return l.GetColumn(columnName).EnumToTextConversion
   264  }
   265  
   266  // UniqueKey is the combination of a key's name and columns
   267  type UniqueKey struct {
   268  	Name            string
   269  	Columns         ColumnList
   270  	HasNullable     bool
   271  	HasFloat        bool
   272  	IsAutoIncrement bool
   273  }
   274  
   275  // IsPrimary checks if this unique key is primary
   276  func (k *UniqueKey) IsPrimary() bool {
   277  	return k.Name == "PRIMARY"
   278  }
   279  
   280  // Len returns the length of this list
   281  func (k *UniqueKey) Len() int {
   282  	return k.Columns.Len()
   283  }
   284  
   285  // String returns a visual representation of this key
   286  func (k *UniqueKey) String() string {
   287  	description := k.Name
   288  	if k.IsAutoIncrement {
   289  		description = fmt.Sprintf("%s (auto_increment)", description)
   290  	}
   291  	return fmt.Sprintf("%s: %s; has nullable: %+v", description, k.Columns.Names(), k.HasNullable)
   292  }