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  }