vitess.io/vitess@v0.16.2/go/vt/topotools/vschema_ddl.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package topotools 18 19 import ( 20 "reflect" 21 22 "vitess.io/vitess/go/vt/sqlparser" 23 "vitess.io/vitess/go/vt/vterrors" 24 25 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 26 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 27 ) 28 29 // ApplyVSchemaDDL applies the given DDL statement to the vschema 30 // keyspace definition and returns the modified keyspace object. 31 func ApplyVSchemaDDL(ksName string, ks *vschemapb.Keyspace, alterVschema *sqlparser.AlterVschema) (*vschemapb.Keyspace, error) { 32 if ks == nil { 33 ks = new(vschemapb.Keyspace) 34 } 35 36 if ks.Tables == nil { 37 ks.Tables = map[string]*vschemapb.Table{} 38 } 39 40 if ks.Vindexes == nil { 41 ks.Vindexes = map[string]*vschemapb.Vindex{} 42 } 43 44 var tableName string 45 var table *vschemapb.Table 46 if !alterVschema.Table.IsEmpty() { 47 tableName = alterVschema.Table.Name.String() 48 table = ks.Tables[tableName] 49 } 50 51 switch alterVschema.Action { 52 case sqlparser.CreateVindexDDLAction: 53 name := alterVschema.VindexSpec.Name.String() 54 if _, ok := ks.Vindexes[name]; ok { 55 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s already exists in keyspace %s", name, ksName) 56 } 57 58 // Make sure the keyspace has the sharded bit set to true 59 // if this is the first vindex defined in the keyspace. 60 if len(ks.Vindexes) == 0 { 61 ks.Sharded = true 62 } 63 64 owner, params := alterVschema.VindexSpec.ParseParams() 65 ks.Vindexes[name] = &vschemapb.Vindex{ 66 Type: alterVschema.VindexSpec.Type.String(), 67 Params: params, 68 Owner: owner, 69 } 70 71 return ks, nil 72 73 case sqlparser.DropVindexDDLAction: 74 name := alterVschema.VindexSpec.Name.String() 75 if _, ok := ks.Vindexes[name]; !ok { 76 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s does not exists in keyspace %s", name, ksName) 77 } 78 79 for tableName, table := range ks.Tables { 80 // Make sure there isn't a vindex with the same name left on the table. 81 for _, vindex := range table.ColumnVindexes { 82 if vindex.Name == name { 83 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "can not drop vindex cause %s still defined on table %s", name, tableName) 84 } 85 } 86 } 87 88 delete(ks.Vindexes, name) 89 90 return ks, nil 91 92 case sqlparser.AddVschemaTableDDLAction: 93 if ks.Sharded { 94 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "add vschema table: unsupported on sharded keyspace %s", ksName) 95 } 96 97 name := alterVschema.Table.Name.String() 98 if _, ok := ks.Tables[name]; ok { 99 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema already contains table %s in keyspace %s", name, ksName) 100 } 101 102 ks.Tables[name] = &vschemapb.Table{} 103 104 return ks, nil 105 106 case sqlparser.DropVschemaTableDDLAction: 107 name := alterVschema.Table.Name.String() 108 if _, ok := ks.Tables[name]; !ok { 109 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema does not contain table %s in keyspace %s", name, ksName) 110 } 111 112 delete(ks.Tables, name) 113 114 return ks, nil 115 116 case sqlparser.AddColVindexDDLAction: 117 // Support two cases: 118 // 119 // 1. The vindex type / params / owner are specified. If the 120 // named vindex doesn't exist, create it. If it does exist, 121 // require the parameters to match. 122 // 123 // 2. The vindex type is not specified. Make sure the vindex 124 // already exists. 125 spec := alterVschema.VindexSpec 126 name := spec.Name.String() 127 if !spec.Type.IsEmpty() { 128 owner, params := spec.ParseParams() 129 if vindex, ok := ks.Vindexes[name]; ok { 130 if vindex.Type != spec.Type.String() { 131 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s defined with type %s not %s", name, vindex.Type, spec.Type.String()) 132 } 133 if vindex.Owner != owner { 134 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s defined with owner %s not %s", name, vindex.Owner, owner) 135 } 136 if (len(vindex.Params) != 0 || len(params) != 0) && !reflect.DeepEqual(vindex.Params, params) { 137 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s defined with different parameters", name) 138 } 139 } else { 140 // Make sure the keyspace has the sharded bit set to true 141 // if this is the first vindex defined in the keyspace. 142 if len(ks.Vindexes) == 0 { 143 ks.Sharded = true 144 } 145 ks.Vindexes[name] = &vschemapb.Vindex{ 146 Type: spec.Type.String(), 147 Params: params, 148 Owner: owner, 149 } 150 } 151 } else { 152 if _, ok := ks.Vindexes[name]; !ok { 153 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s does not exist in keyspace %s", name, ksName) 154 } 155 } 156 157 // If this is the first vindex being defined on the table, create 158 // the empty table record 159 if table == nil { 160 table = &vschemapb.Table{ 161 ColumnVindexes: make([]*vschemapb.ColumnVindex, 0, 4), 162 } 163 } 164 165 // Make sure there isn't already a vindex with the same name on 166 // this table. 167 for _, vindex := range table.ColumnVindexes { 168 if vindex.Name == name { 169 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s already defined on table %s", name, tableName) 170 } 171 } 172 173 columns := make([]string, len(alterVschema.VindexCols)) 174 for i, col := range alterVschema.VindexCols { 175 columns[i] = col.String() 176 } 177 table.ColumnVindexes = append(table.ColumnVindexes, &vschemapb.ColumnVindex{ 178 Name: name, 179 Columns: columns, 180 }) 181 ks.Tables[tableName] = table 182 183 return ks, nil 184 185 case sqlparser.DropColVindexDDLAction: 186 spec := alterVschema.VindexSpec 187 name := spec.Name.String() 188 if table == nil { 189 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "table %s.%s not defined in vschema", ksName, tableName) 190 } 191 192 for i, colVindex := range table.ColumnVindexes { 193 if colVindex.Name == name { 194 table.ColumnVindexes = append(table.ColumnVindexes[:i], table.ColumnVindexes[i+1:]...) 195 if len(table.ColumnVindexes) == 0 { 196 delete(ks.Tables, tableName) 197 } 198 return ks, nil 199 } 200 } 201 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex %s not defined in table %s.%s", name, ksName, tableName) 202 203 case sqlparser.AddSequenceDDLAction: 204 if ks.Sharded { 205 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "add sequence table: unsupported on sharded keyspace %s", ksName) 206 } 207 208 name := alterVschema.Table.Name.String() 209 if _, ok := ks.Tables[name]; ok { 210 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema already contains sequence %s in keyspace %s", name, ksName) 211 } 212 213 ks.Tables[name] = &vschemapb.Table{Type: "sequence"} 214 215 return ks, nil 216 217 case sqlparser.AddAutoIncDDLAction: 218 name := alterVschema.Table.Name.String() 219 table := ks.Tables[name] 220 if table == nil { 221 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema does not contain table %s in keyspace %s", name, ksName) 222 } 223 224 if table.AutoIncrement != nil { 225 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema already contains auto inc %v on table %s in keyspace %s", table.AutoIncrement, name, ksName) 226 } 227 228 table.AutoIncrement = &vschemapb.AutoIncrement{ 229 Column: alterVschema.AutoIncSpec.Column.String(), 230 Sequence: sqlparser.String(alterVschema.AutoIncSpec.Sequence), 231 } 232 233 return ks, nil 234 } 235 236 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected vindex ddl operation %s", alterVschema.Action.ToString()) 237 }