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 }