github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/alter_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 plan 16 17 import ( 18 "fmt" 19 "strings" 20 21 "gopkg.in/src-d/go-errors.v1" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/expression" 25 "github.com/dolthub/go-mysql-server/sql/types" 26 ) 27 28 var ( 29 // ErrIndexActionNotImplemented is returned when the action has not been implemented 30 ErrIndexActionNotImplemented = errors.NewKind("alter table index action is not implemented: %v") 31 // ErrCreateIndexMissingColumns is returned when a CREATE INDEX statement does not provide any columns 32 ErrCreateIndexMissingColumns = errors.NewKind("cannot create an index without columns") 33 // ErrCreateIndexNonExistentColumn is returned when a key is provided in the index that isn't in the table 34 ErrCreateIndexNonExistentColumn = errors.NewKind("column `%v` does not exist in the table") 35 // ErrCreateIndexDuplicateColumn is returned when a CREATE INDEX statement has the same column multiple times 36 ErrCreateIndexDuplicateColumn = errors.NewKind("cannot have duplicates of columns in an index: `%v`") 37 ) 38 39 type IndexAction byte 40 41 const ( 42 IndexAction_Create IndexAction = iota 43 IndexAction_Drop 44 IndexAction_Rename 45 IndexAction_DisableEnableKeys 46 ) 47 48 type AlterIndex struct { 49 // Action states whether it's a CREATE, DROP, or RENAME 50 Action IndexAction 51 // ddlNode references to the database that is being operated on 52 ddlNode 53 // Table is the table that is being referenced 54 Table sql.Node 55 // IndexName is the index name, and in the case of a RENAME it represents the new name 56 IndexName string 57 // PreviousIndexName states the old name when renaming an index 58 PreviousIndexName string 59 // Using states whether you're using BTREE, HASH, or none 60 Using sql.IndexUsing 61 // Constraint specifies whether this is UNIQUE, FULLTEXT, SPATIAL, or none 62 Constraint sql.IndexConstraint 63 // Columns contains the column names (and possibly lengths) when creating an index 64 Columns []sql.IndexColumn 65 // Comment is the comment that was left at index creation, if any 66 Comment string 67 // DisableKeys determines whether to DISABLE KEYS if true or ENABLE KEYS if false 68 DisableKeys bool 69 // TargetSchema Analyzer state. 70 targetSchema sql.Schema 71 } 72 73 var _ sql.SchemaTarget = (*AlterIndex)(nil) 74 var _ sql.Expressioner = (*AlterIndex)(nil) 75 var _ sql.Node = (*AlterIndex)(nil) 76 var _ sql.CollationCoercible = (*AlterIndex)(nil) 77 78 func NewAlterCreateIndex(db sql.Database, table sql.Node, indexName string, using sql.IndexUsing, constraint sql.IndexConstraint, columns []sql.IndexColumn, comment string) *AlterIndex { 79 return &AlterIndex{ 80 Action: IndexAction_Create, 81 ddlNode: ddlNode{Db: db}, 82 Table: table, 83 IndexName: indexName, 84 Using: using, 85 Constraint: constraint, 86 Columns: columns, 87 Comment: comment, 88 } 89 } 90 91 func NewAlterDropIndex(db sql.Database, table sql.Node, indexName string) *AlterIndex { 92 return &AlterIndex{ 93 Action: IndexAction_Drop, 94 ddlNode: ddlNode{Db: db}, 95 Table: table, 96 IndexName: indexName, 97 } 98 } 99 100 func NewAlterRenameIndex(db sql.Database, table sql.Node, fromIndexName, toIndexName string) *AlterIndex { 101 return &AlterIndex{ 102 Action: IndexAction_Rename, 103 ddlNode: ddlNode{Db: db}, 104 Table: table, 105 IndexName: toIndexName, 106 PreviousIndexName: fromIndexName, 107 } 108 } 109 110 func NewAlterDisableEnableKeys(db sql.Database, table sql.Node, disableKeys bool) *AlterIndex { 111 return &AlterIndex{ 112 Action: IndexAction_DisableEnableKeys, 113 ddlNode: ddlNode{Db: db}, 114 Table: table, 115 DisableKeys: disableKeys, 116 } 117 } 118 119 // Schema implements the Node interface. 120 func (p *AlterIndex) Schema() sql.Schema { 121 return types.OkResultSchema 122 } 123 124 // WithChildren implements the Node interface. For AlterIndex, the only appropriate input is 125 // a single child - The Table. 126 func (p AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) { 127 if len(children) != 1 { 128 return nil, sql.ErrInvalidChildrenNumber.New(p, len(children), 1) 129 } 130 131 switch p.Action { 132 case IndexAction_Create, IndexAction_Drop, IndexAction_Rename, IndexAction_DisableEnableKeys: 133 p.Table = children[0] 134 return &p, nil 135 default: 136 return nil, ErrIndexActionNotImplemented.New(p.Action) 137 } 138 } 139 140 func (p AlterIndex) WithTargetSchema(schema sql.Schema) (sql.Node, error) { 141 p.targetSchema = schema 142 return &p, nil 143 } 144 145 func (p *AlterIndex) TargetSchema() sql.Schema { 146 return p.targetSchema 147 } 148 149 // Expressions on the AlterIndex object are specifically column default expresions, Nothing else. 150 func (p *AlterIndex) Expressions() []sql.Expression { 151 newExprs := make([]sql.Expression, len(p.TargetSchema())) 152 for i, col := range p.TargetSchema() { 153 newExprs[i] = expression.WrapExpression(col.Default) 154 } 155 156 return newExprs 157 } 158 159 // WithExpressions implements the Node Interface. For AlterIndex, expressions represent column defaults on the 160 // targetSchema instance - required to be the same number of columns on the target schema. 161 func (p AlterIndex) WithExpressions(expressions ...sql.Expression) (sql.Node, error) { 162 columns := p.TargetSchema().Copy() 163 164 if len(columns) != len(expressions) { 165 return nil, fmt.Errorf("invariant failure: column count does not match expression count") 166 } 167 168 for i, expr := range expressions { 169 wrapper, ok := expr.(*expression.Wrapper) 170 if !ok { 171 return nil, fmt.Errorf("*expression.Wrapper cast failure unexpected: %v", expr) 172 } 173 174 wrapped := wrapper.Unwrap() 175 if wrapped == nil { 176 continue // No default for this column 177 } 178 179 newColDef, ok := wrapped.(*sql.ColumnDefaultValue) 180 if !ok { 181 return nil, fmt.Errorf("*sql.ColumnDefaultValue cast failure unexptected: %v", wrapped) 182 } 183 184 columns[i].Default = newColDef 185 } 186 187 newIdx, err := p.WithTargetSchema(columns) 188 if err != nil { 189 return nil, err 190 } 191 return newIdx, nil 192 } 193 194 // CheckPrivileges implements the interface sql.Node. 195 func (p *AlterIndex) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { 196 subject := sql.PrivilegeCheckSubject{ 197 Database: CheckPrivilegeNameForDatabase(p.ddlNode.Database()), 198 Table: getTableName(p.Table), 199 } 200 return opChecker.UserHasPrivileges(ctx, 201 sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Index)) 202 } 203 204 // CollationCoercibility implements the interface sql.CollationCoercible. 205 func (*AlterIndex) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 206 return sql.Collation_binary, 7 207 } 208 209 // WithDatabase implements the sql.Databaser interface. 210 func (p *AlterIndex) WithDatabase(database sql.Database) (sql.Node, error) { 211 np := *p 212 np.Db = database 213 return &np, nil 214 } 215 216 func (p AlterIndex) String() string { 217 pr := sql.NewTreePrinter() 218 switch p.Action { 219 case IndexAction_Create: 220 _ = pr.WriteNode("CreateIndex(%s)", p.IndexName) 221 children := []string{fmt.Sprintf("Table(%s)", p.Table.String())} 222 switch p.Constraint { 223 case sql.IndexConstraint_Unique: 224 children = append(children, "Constraint(UNIQUE)") 225 case sql.IndexConstraint_Spatial: 226 children = append(children, "Constraint(SPATIAL)") 227 case sql.IndexConstraint_Fulltext: 228 children = append(children, "Constraint(FULLTEXT)") 229 } 230 switch p.Using { 231 case sql.IndexUsing_BTree, sql.IndexUsing_Default: 232 children = append(children, "Using(BTREE)") 233 case sql.IndexUsing_Hash: 234 children = append(children, "Using(HASH)") 235 } 236 cols := make([]string, len(p.Columns)) 237 for i, col := range p.Columns { 238 if col.Length == 0 { 239 cols[i] = col.Name 240 } else { 241 cols[i] = fmt.Sprintf("%s(%v)", col.Name, col.Length) 242 } 243 } 244 children = append(children, fmt.Sprintf("Columns(%s)", strings.Join(cols, ", "))) 245 children = append(children, fmt.Sprintf("Comment(%s)", p.Comment)) 246 _ = pr.WriteChildren(children...) 247 case IndexAction_Drop: 248 _ = pr.WriteNode("DropIndex(%s)", p.IndexName) 249 _ = pr.WriteChildren(fmt.Sprintf("Table(%s)", p.Table.String())) 250 case IndexAction_Rename: 251 _ = pr.WriteNode("RenameIndex") 252 _ = pr.WriteChildren( 253 fmt.Sprintf("Table(%s)", p.Table.String()), 254 fmt.Sprintf("FromIndex(%s)", p.PreviousIndexName), 255 fmt.Sprintf("ToIndex(%s)", p.IndexName), 256 ) 257 default: 258 _ = pr.WriteNode("Unknown_Index_Action(%v)", p.Action) 259 } 260 return pr.String() 261 } 262 263 func (p *AlterIndex) Resolved() bool { 264 return p.Table.Resolved() && p.ddlNode.Resolved() && p.targetSchema.Resolved() 265 } 266 267 func (p *AlterIndex) IsReadOnly() bool { 268 return false 269 } 270 271 // Children implements the sql.Node interface. 272 func (p *AlterIndex) Children() []sql.Node { 273 return []sql.Node{p.Table} 274 } 275 276 // ColumnNames returns each column's name without the length property. 277 func (p *AlterIndex) ColumnNames() []string { 278 colNames := make([]string, len(p.Columns)) 279 for i, col := range p.Columns { 280 colNames[i] = col.Name 281 } 282 return colNames 283 }