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  }