github.com/dolthub/go-mysql-server@v0.18.0/sql/index.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 "fmt" 19 "strings" 20 ) 21 22 type IndexDef struct { 23 Name string 24 Columns []IndexColumn 25 Constraint IndexConstraint 26 Storage IndexUsing 27 Comment string 28 } 29 30 // IndexColumn is the column by which to add to an index. 31 type IndexColumn struct { 32 Name string 33 // Length represents the index prefix length. If zero, then no length was specified. 34 Length int64 35 } 36 37 // IndexConstraint represents any constraints that should be applied to the index. 38 type IndexConstraint byte 39 40 const ( 41 IndexConstraint_None IndexConstraint = iota 42 IndexConstraint_Unique 43 IndexConstraint_Fulltext 44 IndexConstraint_Spatial 45 IndexConstraint_Primary 46 ) 47 48 // IndexUsing is the desired storage type. 49 type IndexUsing byte 50 51 const ( 52 IndexUsing_Default IndexUsing = iota 53 IndexUsing_BTree 54 IndexUsing_Hash 55 ) 56 57 // Index is the representation of an index, and also creates an IndexLookup when given a collection of ranges. 58 type Index interface { 59 // ID returns the identifier of the index. 60 ID() string 61 // Database returns the database name this index belongs to. 62 Database() string 63 // Table returns the table name this index belongs to. 64 Table() string 65 // Expressions returns the indexed expressions. If the result is more than 66 // one expression, it means the index has multiple columns indexed. If it's 67 // just one, it means it may be an expression or a column. 68 Expressions() []string 69 // IsUnique returns whether this index is unique 70 IsUnique() bool 71 // IsSpatial returns whether this index is a spatial index 72 IsSpatial() bool 73 // IsFullText returns whether this index is a Full-Text index 74 IsFullText() bool 75 // Comment returns the comment for this index 76 Comment() string 77 // IndexType returns the type of this index, e.g. BTREE 78 IndexType() string 79 // IsGenerated returns whether this index was generated. Generated indexes 80 // are used for index access, but are not displayed (such as with SHOW INDEXES). 81 IsGenerated() bool 82 // ColumnExpressionTypes returns each expression and its associated Type. 83 // Each expression string should exactly match the string returned from 84 // Index.Expressions(). 85 ColumnExpressionTypes() []ColumnExpressionType 86 // CanSupport returns whether this index supports lookups on the given 87 // range filters. 88 CanSupport(...Range) bool 89 // PrefixLengths returns the prefix lengths for each column in this index 90 PrefixLengths() []uint16 91 } 92 93 // ExtendedIndex is an extension of Index, that allows access to appended primary keys. MySQL internally represents an 94 // index as the collection of all explicitly referenced columns, while appending any unreferenced primary keys to the 95 // end (in order of their declaration). For full MySQL compatibility, integrators are encouraged to mimic this, however 96 // not all implementations may define their indexes (on tables with primary keys) in this way, therefore this interface 97 // is optional. 98 type ExtendedIndex interface { 99 Index 100 // ExtendedExpressions returns the same result as Expressions, but appends any primary keys that are implicitly in 101 // the index. The appended primary keys are in declaration order. 102 ExtendedExpressions() []string 103 // ExtendedColumnExpressionTypes returns the same result as ColumnExpressionTypes, but appends the type of any 104 // primary keys that are implicitly in the index. The appended primary keys are in declaration order. 105 ExtendedColumnExpressionTypes() []ColumnExpressionType 106 } 107 108 // IndexLookup is the implementation-specific definition of an index lookup. The IndexLookup must contain all necessary 109 // information to retrieve exactly the rows in the table as specified by the ranges given to their parent index. 110 // Implementors are responsible for all semantics of correctly returning rows that match an index lookup. 111 type IndexLookup struct { 112 Index Index 113 Ranges RangeCollection 114 // IsPointLookup is true if the lookup will return one or zero 115 // values; the range is null safe, the index is unique, every index 116 // column has a range expression, and every range expression is an 117 // exact equality. 118 IsPointLookup bool 119 IsEmptyRange bool 120 IsSpatialLookup bool 121 IsReverse bool 122 } 123 124 var emptyLookup = IndexLookup{} 125 126 func NewIndexLookup(idx Index, ranges RangeCollection, isPointLookup, isEmptyRange, isSpatialLookup, isReverse bool) IndexLookup { 127 if isReverse { 128 for i, j := 0, len(ranges)-1; i < j; i, j = i+1, j-1 { 129 ranges[i], ranges[j] = ranges[j], ranges[i] 130 } 131 } 132 return IndexLookup{ 133 Index: idx, 134 Ranges: ranges, 135 IsPointLookup: isPointLookup, 136 IsEmptyRange: isEmptyRange, 137 IsSpatialLookup: isSpatialLookup, 138 IsReverse: isReverse, 139 } 140 } 141 142 func (il IndexLookup) IsEmpty() bool { 143 return il.Index == nil 144 } 145 146 func (il IndexLookup) String() string { 147 pr := NewTreePrinter() 148 _ = pr.WriteNode("IndexLookup") 149 pr.WriteChildren(fmt.Sprintf("index: %s", il.Index), fmt.Sprintf("ranges: %s", il.Ranges.String())) 150 return pr.String() 151 } 152 153 func (il IndexLookup) DebugString() string { 154 pr := NewTreePrinter() 155 _ = pr.WriteNode("IndexLookup") 156 pr.WriteChildren(fmt.Sprintf("index: %s", il.Index), fmt.Sprintf("ranges: %s", il.Ranges.DebugString())) 157 return pr.String() 158 } 159 160 // FilteredIndex is an extension of |Index| that allows an index to declare certain filter predicates handled, 161 // allowing them to be removed from the overall plan for greater execution efficiency 162 type FilteredIndex interface { 163 Index 164 // HandledFilters returns a subset of |filters| that are satisfied 165 // by index lookups to this index. 166 HandledFilters(filters []Expression) (handled []Expression) 167 } 168 169 type IndexOrder byte 170 171 const ( 172 IndexOrderNone IndexOrder = iota 173 IndexOrderAsc 174 IndexOrderDesc 175 ) 176 177 // OrderedIndex is an extension of |Index| that allows indexes to declare their return order. The query engine can 178 // optimize certain queries if the order of an index is guaranteed, e.g. removing a sort operation. 179 type OrderedIndex interface { 180 Index 181 // Order returns the order of results for reads from this index 182 Order() IndexOrder 183 // Reversible returns whether or not this index can be iterated on backwards 184 Reversible() bool 185 } 186 187 // ColumnExpressionType returns a column expression along with its Type. 188 type ColumnExpressionType struct { 189 Expression string 190 Type Type 191 } 192 193 // ValidatePrimaryKeyDrop validates that a primary key may be dropped. If any validation error is returned, then it 194 // means it is not valid to drop this table's primary key. Validation includes checking for PK columns with the 195 // auto_increment property, in which case, MySQL requires that another index exists on the table where the first 196 // column in the index is the auto_increment column from the primary key. 197 // https://dev.mysql.com/doc/refman/8.0/en/innodb-auto-increment-handling.html 198 func ValidatePrimaryKeyDrop(ctx *Context, t IndexAddressableTable, oldSchema PrimaryKeySchema) error { 199 // If the primary key doesn't have an auto_increment option set, then we don't validate anything else 200 autoIncrementColumn := findPrimaryKeyAutoIncrementColumn(oldSchema) 201 if autoIncrementColumn == nil { 202 return nil 203 } 204 205 // If there is an auto_increment option set, then we need to verify that there is still a supporting index, 206 // meaning the index is prefixed with the primary key column that contains the auto_increment option. 207 indexes, err := t.GetIndexes(ctx) 208 if err != nil { 209 return err 210 } 211 212 for _, idx := range indexes { 213 // Don't bother considering FullText or Spatial indexes, since these aren't valid 214 // on auto_increment int columns anyway. 215 if idx.IsFullText() || idx.IsSpatial() { 216 continue 217 } 218 219 // Skip the primary key index, since we're trying to delete it 220 if strings.ToLower(idx.ID()) == "primary" { 221 continue 222 } 223 224 if idx.Expressions()[0] == autoIncrementColumn.Source+"."+autoIncrementColumn.Name { 225 // By this point, we've verified that it's valid to drop the table's primary key 226 return nil 227 } 228 } 229 230 // We've searched all indexes and couldn't find one supporting the auto_increment column, so we error out. 231 return ErrWrongAutoKey.New() 232 } 233 234 // findPrimaryKeyAutoIncrementColumn returns the first column in the primary key that has the auto_increment option, 235 // otherwise it returns null if no primary key columns are defined with the auto_increment option. 236 func findPrimaryKeyAutoIncrementColumn(schema PrimaryKeySchema) *Column { 237 for _, ordinal := range schema.PkOrdinals { 238 if schema.Schema[ordinal].AutoIncrement { 239 return schema.Schema[ordinal] 240 } 241 } 242 return nil 243 }