github.com/dolthub/go-mysql-server@v0.18.0/sql/schemas.go (about) 1 // Copyright 2020-2021 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 sql 16 17 import ( 18 "reflect" 19 "strings" 20 21 "gopkg.in/src-d/go-errors.v1" 22 ) 23 24 var ( 25 // ErrUnexpectedType is thrown when a received type is not the expected 26 ErrUnexpectedType = errors.NewKind("value at %d has unexpected type: %s") 27 ) 28 29 // MaxIdentifierLength is the maximum number of characters permissible in MySQL identifiers, like column or table names 30 const MaxIdentifierLength = 64 31 32 // Schema is the definition of a table. 33 type Schema []*Column 34 35 // CheckRow checks the row conforms to the schema. 36 func (s Schema) CheckRow(row Row) error { 37 expected := len(s) 38 got := len(row) 39 if expected != got { 40 return ErrUnexpectedRowLength.New(expected, got) 41 } 42 43 for idx, f := range s { 44 v := row[idx] 45 if f.Check(v) { 46 continue 47 } 48 49 typ := reflect.TypeOf(v).String() 50 return ErrUnexpectedType.New(idx, typ) 51 } 52 53 return nil 54 } 55 56 // HasVirtualColumns returns whether the schema has any virtual columns 57 func (s Schema) HasVirtualColumns() bool { 58 for _, col := range s { 59 if col.Virtual { 60 return true 61 } 62 } 63 return false 64 } 65 66 // PhysicalSchema returns a schema with only the physical (non-virtual) columns 67 func (s Schema) PhysicalSchema() Schema { 68 var physical Schema 69 for _, col := range s { 70 if !col.Virtual { 71 physical = append(physical, col) 72 } 73 } 74 return physical 75 } 76 77 // Copy returns a deep copy of this schema, making a copy of all columns 78 func (s Schema) Copy() Schema { 79 ns := make(Schema, len(s)) 80 for i, col := range s { 81 ns[i] = col.Copy() 82 } 83 return ns 84 } 85 86 // Contains returns whether the schema contains a column with the given name. 87 func (s Schema) Contains(column string, source string) bool { 88 return s.IndexOf(column, source) >= 0 89 } 90 91 // IndexOf returns the index of the given column in the schema or -1 if it's not present. 92 func (s Schema) IndexOf(column, source string) int { 93 column = strings.ToLower(column) 94 source = strings.ToLower(source) 95 for i, col := range s { 96 if strings.ToLower(col.Name) == column && strings.ToLower(col.Source) == source { 97 return i 98 } 99 } 100 return -1 101 } 102 103 // IndexOfColName returns the index of the given column in the schema or -1 if it's not present. Only safe for schemas 104 // corresponding to a single table, where the source of the column is irrelevant. 105 func (s Schema) IndexOfColName(column string) int { 106 column = strings.ToLower(column) 107 for i, col := range s { 108 if strings.ToLower(col.Name) == column { 109 return i 110 } 111 } 112 return -1 113 } 114 115 // Equals checks whether the given schema is equal to this one. 116 func (s Schema) Equals(s2 Schema) bool { 117 if len(s) != len(s2) { 118 return false 119 } 120 121 for i := range s { 122 if !s[i].Equals(s2[i]) { 123 return false 124 } 125 } 126 127 return true 128 } 129 130 // CaseSensitiveEquals checks whether the given schema is equal to this one, 131 // failing for column names with different casing 132 func (s Schema) CaseSensitiveEquals(s2 Schema) bool { 133 if len(s) != len(s2) { 134 return false 135 } 136 137 for i := range s { 138 if s[i].Name != s2[i].Name { 139 return false 140 } 141 if !s[i].Equals(s2[i]) { 142 return false 143 } 144 } 145 146 return true 147 } 148 149 // HasAutoIncrement returns true if the schema has an auto increment column. 150 func (s Schema) HasAutoIncrement() bool { 151 for _, c := range s { 152 if c.AutoIncrement { 153 return true 154 } 155 } 156 157 return false 158 } 159 160 // Resolved returns true if this schema is fully resolved. Currently, the only piece of a schema that needs 161 // to be resolved are any column default value expressions. 162 func (s Schema) Resolved() bool { 163 for _, c := range s { 164 if c.Default != nil { 165 if !c.Default.Resolved() { 166 return false 167 } 168 } 169 } 170 171 return true 172 } 173 174 func IsKeyless(s Schema) bool { 175 for _, c := range s { 176 if c.PrimaryKey { 177 return false 178 } 179 } 180 181 return true 182 } 183 184 // PrimaryKeySchema defines table metadata for columns and primary key ordering 185 type PrimaryKeySchema struct { 186 Schema 187 PkOrdinals []int 188 } 189 190 // NewPrimaryKeySchema constructs a new PrimaryKeySchema. PK ordinals 191 // default to the in-order set read from the Schema. 192 func NewPrimaryKeySchema(s Schema, pkOrds ...int) PrimaryKeySchema { 193 if len(pkOrds) == 0 { 194 pkOrds = make([]int, 0) 195 for i, c := range s { 196 if c.PrimaryKey { 197 pkOrds = append(pkOrds, i) 198 } 199 } 200 } 201 return PrimaryKeySchema{Schema: s, PkOrdinals: pkOrds} 202 } 203 204 // SchemaToPrimaryKeySchema adapts the schema given to a PrimaryKey schema using the primary keys of the table given, if 205 // present. The resulting PrimaryKeySchema may have an empty key set if the table has no primary keys. Matching for 206 // ordinals is performed by column name, with the aid of |renames| when provided. 207 func SchemaToPrimaryKeySchema(table Table, sch Schema, renames ...ColumnRename) PrimaryKeySchema { 208 var pks []*Column 209 if pkt, ok := table.(PrimaryKeyTable); ok { 210 schema := pkt.PrimaryKeySchema() 211 for _, ordinal := range schema.PkOrdinals { 212 pks = append(pks, schema.Schema[ordinal]) 213 } 214 } else { 215 // set PkOrdinals by schema order 216 return NewPrimaryKeySchema(sch) 217 } 218 219 mapping := make(map[string]string) 220 for _, r := range renames { 221 mapping[strings.ToLower(r.Before)] = r.After 222 } 223 224 ords := make([]int, len(pks)) 225 for i, pk := range pks { 226 name := strings.ToLower(pk.Name) 227 if n, ok := mapping[name]; ok { 228 name = n 229 } 230 ords[i] = sch.IndexOf(name, pk.Source) 231 } 232 return NewPrimaryKeySchema(sch, ords...) 233 } 234 235 // ColumnOrder is used in ALTER TABLE statements to change the order of inserted / modified columns. 236 type ColumnOrder struct { 237 First bool // True if this column should come first 238 AfterColumn string // Set to the name of the column after which this column should appear 239 } 240 241 type ColumnRename struct { 242 Before, After string 243 }