github.com/XiaoMi/Gaea@v1.2.5/parser/model/model.go (about)

     1  // Copyright 2015 PingCAP, 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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package model
    15  
    16  import (
    17  	"encoding/json"
    18  	"strings"
    19  	"time"
    20  
    21  	"github.com/pingcap/errors"
    22  	tipb "github.com/pingcap/tipb/go-tipb"
    23  
    24  	"github.com/XiaoMi/Gaea/mysql"
    25  	"github.com/XiaoMi/Gaea/parser/auth"
    26  	"github.com/XiaoMi/Gaea/parser/types"
    27  )
    28  
    29  // SchemaState is the state for schema elements.
    30  type SchemaState byte
    31  
    32  const (
    33  	// StateNone means this schema element is absent and can't be used.
    34  	StateNone SchemaState = iota
    35  	// StateDeleteOnly means we can only delete items for this schema element.
    36  	StateDeleteOnly
    37  	// StateWriteOnly means we can use any write operation on this schema element,
    38  	// but outer can't read the changed data.
    39  	StateWriteOnly
    40  	// StateWriteReorganization means we are re-organizing whole data after write only state.
    41  	StateWriteReorganization
    42  	// StateDeleteReorganization means we are re-organizing whole data after delete only state.
    43  	StateDeleteReorganization
    44  	// StatePublic means this schema element is ok for all write and read operations.
    45  	StatePublic
    46  )
    47  
    48  // String implements fmt.Stringer interface.
    49  func (s SchemaState) String() string {
    50  	switch s {
    51  	case StateDeleteOnly:
    52  		return "delete only"
    53  	case StateWriteOnly:
    54  		return "write only"
    55  	case StateWriteReorganization:
    56  		return "write reorganization"
    57  	case StateDeleteReorganization:
    58  		return "delete reorganization"
    59  	case StatePublic:
    60  		return "public"
    61  	default:
    62  		return "none"
    63  	}
    64  }
    65  
    66  const (
    67  	// ColumnInfoVersion0 means the column info version is 0.
    68  	ColumnInfoVersion0 = uint64(0)
    69  	// ColumnInfoVersion1 means the column info version is 1.
    70  	ColumnInfoVersion1 = uint64(1)
    71  )
    72  
    73  // ColumnInfo provides meta data describing of a table column.
    74  type ColumnInfo struct {
    75  	ID                  int64               `json:"id"`
    76  	Name                CIStr               `json:"name"`
    77  	Offset              int                 `json:"offset"`
    78  	OriginDefaultValue  interface{}         `json:"origin_default"`
    79  	DefaultValue        interface{}         `json:"default"`
    80  	DefaultValueBit     []byte              `json:"default_bit"`
    81  	GeneratedExprString string              `json:"generated_expr_string"`
    82  	GeneratedStored     bool                `json:"generated_stored"`
    83  	Dependences         map[string]struct{} `json:"dependences"`
    84  	types.FieldType     `json:"type"`
    85  	State               SchemaState `json:"state"`
    86  	Comment             string      `json:"comment"`
    87  	// Version means the version of the column info.
    88  	// Version = 0: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in system time zone.
    89  	//              That is a bug if multiple TiDB servers in different system time zone.
    90  	// Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone.
    91  	//              This will fix bug in version 0. For compatibility with version 0, we add version field in column info struct.
    92  	Version uint64 `json:"version"`
    93  }
    94  
    95  // Clone clones ColumnInfo.
    96  func (c *ColumnInfo) Clone() *ColumnInfo {
    97  	nc := *c
    98  	return &nc
    99  }
   100  
   101  // IsGenerated returns true if the column is generated column.
   102  func (c *ColumnInfo) IsGenerated() bool {
   103  	return len(c.GeneratedExprString) != 0
   104  }
   105  
   106  // SetDefaultValue sets the default value.
   107  func (c *ColumnInfo) SetDefaultValue(value interface{}) error {
   108  	c.DefaultValue = value
   109  	if c.Tp == mysql.TypeBit {
   110  		// For mysql.TypeBit type, the default value storage format must be a string.
   111  		// Other value such as int must convert to string format first.
   112  		// The mysql.TypeBit type supports the null default value.
   113  		if value == nil {
   114  			return nil
   115  		}
   116  		if v, ok := value.(string); ok {
   117  			c.DefaultValueBit = []byte(v)
   118  			return nil
   119  		}
   120  		return types.ErrInvalidDefault.GenWithStackByArgs(c.Name)
   121  	}
   122  	return nil
   123  }
   124  
   125  // GetDefaultValue gets the default value of the column.
   126  // Default value use to stored in DefaultValue field, but now,
   127  // bit type default value will store in DefaultValueBit for fix bit default value decode/encode bug.
   128  func (c *ColumnInfo) GetDefaultValue() interface{} {
   129  	if c.Tp == mysql.TypeBit && c.DefaultValueBit != nil {
   130  		return string(c.DefaultValueBit)
   131  	}
   132  	return c.DefaultValue
   133  }
   134  
   135  // FindColumnInfo finds ColumnInfo in cols by name.
   136  func FindColumnInfo(cols []*ColumnInfo, name string) *ColumnInfo {
   137  	name = strings.ToLower(name)
   138  	for _, col := range cols {
   139  		if col.Name.L == name {
   140  			return col
   141  		}
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // ExtraHandleID is the column ID of column which we need to append to schema to occupy the handle's position
   148  // for use of execution phase.
   149  const ExtraHandleID = -1
   150  
   151  const (
   152  	// TableInfoVersion0 means the table info version is 0.
   153  	// Upgrade from v2.1.1 or v2.1.2 to v2.1.3 and later, and then execute a "change/modify column" statement
   154  	// that does not specify a charset value for column. Then the following error may be reported:
   155  	// ERROR 1105 (HY000): unsupported modify charset from utf8mb4 to utf8.
   156  	// To eliminate this error, we will not modify the charset of this column
   157  	// when executing a change/modify column statement that does not specify a charset value for column.
   158  	// This behavior is not compatible with MySQL.
   159  	TableInfoVersion0 = uint16(0)
   160  	// TableInfoVersion1 ColumnInfoVersion1 means the table info version is 1.
   161  	// When we execute a change/modify column statement that does not specify a charset value for column,
   162  	// we set the charset of this column to the charset of table. This behavior is compatible with MySQL.
   163  	TableInfoVersion1 = uint16(1)
   164  	// CurrLatestTableInfoVersion means the latest table info in the current TiDB.
   165  	CurrLatestTableInfoVersion = TableInfoVersion1
   166  )
   167  
   168  // ExtraHandleName is the name of ExtraHandle Column.
   169  var ExtraHandleName = NewCIStr("_tidb_rowid")
   170  
   171  // TableInfo provides meta data describing a DB table.
   172  type TableInfo struct {
   173  	ID      int64  `json:"id"`
   174  	Name    CIStr  `json:"name"`
   175  	Charset string `json:"charset"`
   176  	Collate string `json:"collate"`
   177  	// Columns are listed in the order in which they appear in the schema.
   178  	Columns     []*ColumnInfo `json:"cols"`
   179  	Indices     []*IndexInfo  `json:"index_info"`
   180  	ForeignKeys []*FKInfo     `json:"fk_info"`
   181  	State       SchemaState   `json:"state"`
   182  	PKIsHandle  bool          `json:"pk_is_handle"`
   183  	Comment     string        `json:"comment"`
   184  	AutoIncID   int64         `json:"auto_inc_id"`
   185  	MaxColumnID int64         `json:"max_col_id"`
   186  	MaxIndexID  int64         `json:"max_idx_id"`
   187  	// UpdateTS is used to record the timestamp of updating the table's schema information.
   188  	// These changing schema operations don't include 'truncate table' and 'rename table'.
   189  	UpdateTS uint64 `json:"update_timestamp"`
   190  	// OldSchemaID :
   191  	// Because auto increment ID has schemaID as prefix,
   192  	// We need to save original schemaID to keep autoID unchanged
   193  	// while renaming a table from one database to another.
   194  	// TODO: Remove it.
   195  	// Now it only uses for compatibility with the old version that already uses this field.
   196  	OldSchemaID int64 `json:"old_schema_id,omitempty"`
   197  
   198  	// ShardRowIDBits specify if the implicit row ID is sharded.
   199  	ShardRowIDBits uint64
   200  
   201  	Partition *PartitionInfo `json:"partition"`
   202  
   203  	Compression string `json:"compression"`
   204  
   205  	View *ViewInfo `json:"view"`
   206  
   207  	// Version means the version of the table info.
   208  	Version uint16 `json:"version"`
   209  }
   210  
   211  // GetPartitionInfo returns the partition information.
   212  func (t *TableInfo) GetPartitionInfo() *PartitionInfo {
   213  	if t.Partition != nil && t.Partition.Enable {
   214  		return t.Partition
   215  	}
   216  	return nil
   217  }
   218  
   219  // GetUpdateTime gets the table's updating time.
   220  func (t *TableInfo) GetUpdateTime() time.Time {
   221  	return TSConvert2Time(t.UpdateTS)
   222  }
   223  
   224  // GetDBID returns the schema ID that is used to create an allocator.
   225  // TODO: Remove it after removing OldSchemaID.
   226  func (t *TableInfo) GetDBID(dbID int64) int64 {
   227  	if t.OldSchemaID != 0 {
   228  		return t.OldSchemaID
   229  	}
   230  	return dbID
   231  }
   232  
   233  // Clone clones TableInfo.
   234  func (t *TableInfo) Clone() *TableInfo {
   235  	nt := *t
   236  	nt.Columns = make([]*ColumnInfo, len(t.Columns))
   237  	nt.Indices = make([]*IndexInfo, len(t.Indices))
   238  	nt.ForeignKeys = make([]*FKInfo, len(t.ForeignKeys))
   239  
   240  	for i := range t.Columns {
   241  		nt.Columns[i] = t.Columns[i].Clone()
   242  	}
   243  
   244  	for i := range t.Indices {
   245  		nt.Indices[i] = t.Indices[i].Clone()
   246  	}
   247  
   248  	for i := range t.ForeignKeys {
   249  		nt.ForeignKeys[i] = t.ForeignKeys[i].Clone()
   250  	}
   251  
   252  	return &nt
   253  }
   254  
   255  // GetPkName will return the pk name if pk exists.
   256  func (t *TableInfo) GetPkName() CIStr {
   257  	if t.PKIsHandle {
   258  		for _, colInfo := range t.Columns {
   259  			if mysql.HasPriKeyFlag(colInfo.Flag) {
   260  				return colInfo.Name
   261  			}
   262  		}
   263  	}
   264  	return CIStr{}
   265  }
   266  
   267  // GetPkColInfo gets the ColumnInfo of pk if exists.
   268  // Make sure PkIsHandle checked before call this method.
   269  func (t *TableInfo) GetPkColInfo() *ColumnInfo {
   270  	for _, colInfo := range t.Columns {
   271  		if mysql.HasPriKeyFlag(colInfo.Flag) {
   272  			return colInfo
   273  		}
   274  	}
   275  	return nil
   276  }
   277  
   278  // GetAutoIncrementColInfo return auto increment column info
   279  func (t *TableInfo) GetAutoIncrementColInfo() *ColumnInfo {
   280  	for _, colInfo := range t.Columns {
   281  		if mysql.HasAutoIncrementFlag(colInfo.Flag) {
   282  			return colInfo
   283  		}
   284  	}
   285  	return nil
   286  }
   287  
   288  // IsAutoIncColUnsigned check if auto increment column unsigned
   289  func (t *TableInfo) IsAutoIncColUnsigned() bool {
   290  	col := t.GetAutoIncrementColInfo()
   291  	if col == nil {
   292  		return false
   293  	}
   294  	return mysql.HasUnsignedFlag(col.Flag)
   295  }
   296  
   297  // Cols returns the columns of the table in public state.
   298  func (t *TableInfo) Cols() []*ColumnInfo {
   299  	publicColumns := make([]*ColumnInfo, len(t.Columns))
   300  	maxOffset := -1
   301  	for _, col := range t.Columns {
   302  		if col.State != StatePublic {
   303  			continue
   304  		}
   305  		publicColumns[col.Offset] = col
   306  		if maxOffset < col.Offset {
   307  			maxOffset = col.Offset
   308  		}
   309  	}
   310  	return publicColumns[0 : maxOffset+1]
   311  }
   312  
   313  // NewExtraHandleColInfo mocks a column info for extra handle column.
   314  func NewExtraHandleColInfo() *ColumnInfo {
   315  	colInfo := &ColumnInfo{
   316  		ID:   ExtraHandleID,
   317  		Name: ExtraHandleName,
   318  	}
   319  	colInfo.Flag = mysql.PriKeyFlag
   320  	colInfo.Tp = mysql.TypeLonglong
   321  	colInfo.Flen, colInfo.Decimal = mysql.GetDefaultFieldLengthAndDecimal(mysql.TypeLonglong)
   322  	return colInfo
   323  }
   324  
   325  // ColumnIsInIndex checks whether c is included in any indices of t.
   326  func (t *TableInfo) ColumnIsInIndex(c *ColumnInfo) bool {
   327  	for _, index := range t.Indices {
   328  		for _, column := range index.Columns {
   329  			if column.Name.L == c.Name.L {
   330  				return true
   331  			}
   332  		}
   333  	}
   334  	return false
   335  }
   336  
   337  // IsView checks if tableinfo is a view
   338  func (t *TableInfo) IsView() bool {
   339  	return t.View != nil
   340  }
   341  
   342  // ViewAlgorithm is VIEW's SQL AlGORITHM characteristic.
   343  // See https://dev.mysql.com/doc/refman/5.7/en/view-algorithms.html
   344  type ViewAlgorithm int
   345  
   346  const (
   347  	// AlgorithmUndefined undefined algorithm
   348  	AlgorithmUndefined ViewAlgorithm = iota
   349  	// AlgorithmMerge merge algorithm
   350  	AlgorithmMerge
   351  	// AlgorithmTemptable temptable algorithm
   352  	AlgorithmTemptable
   353  )
   354  
   355  func (v *ViewAlgorithm) String() string {
   356  	switch *v {
   357  	case AlgorithmMerge:
   358  		return "MERGE"
   359  	case AlgorithmTemptable:
   360  		return "TEMPTABLE"
   361  	case AlgorithmUndefined:
   362  		return "UNDEFINED"
   363  	default:
   364  		return "UNDEFINED"
   365  	}
   366  }
   367  
   368  // ViewSecurity is VIEW's SQL SECURITY characteristic.
   369  // See https://dev.mysql.com/doc/refman/5.7/en/create-view.html
   370  type ViewSecurity int
   371  
   372  // view security
   373  const (
   374  	SecurityDefiner ViewSecurity = iota
   375  	SecurityInvoker
   376  )
   377  
   378  func (v *ViewSecurity) String() string {
   379  	switch *v {
   380  	case SecurityInvoker:
   381  		return "INVOKER"
   382  	case SecurityDefiner:
   383  		return "DEFINER"
   384  	default:
   385  		return "DEFINER"
   386  	}
   387  }
   388  
   389  // ViewCheckOption is VIEW's WITH CHECK OPTION clause part.
   390  // See https://dev.mysql.com/doc/refman/5.7/en/view-check-option.html
   391  type ViewCheckOption int
   392  
   393  // view check option
   394  const (
   395  	CheckOptionLocal ViewCheckOption = iota
   396  	CheckOptionCascaded
   397  )
   398  
   399  func (v *ViewCheckOption) String() string {
   400  	switch *v {
   401  	case CheckOptionLocal:
   402  		return "LOCAL"
   403  	case CheckOptionCascaded:
   404  		return "CASCADED"
   405  	default:
   406  		return "CASCADED"
   407  	}
   408  }
   409  
   410  // ViewInfo provides meta data describing a DB view.
   411  type ViewInfo struct {
   412  	Algorithm   ViewAlgorithm      `json:"view_algorithm"`
   413  	Definer     *auth.UserIdentity `json:"view_definer"`
   414  	Security    ViewSecurity       `json:"view_security"`
   415  	SelectStmt  string             `json:"view_select"`
   416  	CheckOption ViewCheckOption    `json:"view_checkoption"`
   417  	Cols        []CIStr            `json:"view_cols"`
   418  }
   419  
   420  // PartitionType is the type for PartitionInfo
   421  type PartitionType int
   422  
   423  // Partition types.
   424  const (
   425  	PartitionTypeRange PartitionType = 1
   426  	PartitionTypeHash  PartitionType = 2
   427  	PartitionTypeList  PartitionType = 3
   428  )
   429  
   430  func (p PartitionType) String() string {
   431  	switch p {
   432  	case PartitionTypeRange:
   433  		return "RANGE"
   434  	case PartitionTypeHash:
   435  		return "HASH"
   436  	case PartitionTypeList:
   437  		return "LIST"
   438  	default:
   439  		return ""
   440  	}
   441  
   442  }
   443  
   444  // PartitionInfo provides table partition info.
   445  type PartitionInfo struct {
   446  	Type    PartitionType `json:"type"`
   447  	Expr    string        `json:"expr"`
   448  	Columns []CIStr       `json:"columns"`
   449  
   450  	// User may already creates table with partition but table partition is not
   451  	// yet supported back then. When Enable is true, write/read need use tid
   452  	// rather than pid.
   453  	Enable bool `json:"enable"`
   454  
   455  	Definitions []PartitionDefinition `json:"definitions"`
   456  	Num         uint64                `json:"num"`
   457  }
   458  
   459  // GetNameByID gets the partition name by ID.
   460  func (pi *PartitionInfo) GetNameByID(id int64) string {
   461  	for _, def := range pi.Definitions {
   462  		if id == def.ID {
   463  			return def.Name.L
   464  		}
   465  	}
   466  	return ""
   467  }
   468  
   469  // PartitionDefinition defines a single partition.
   470  type PartitionDefinition struct {
   471  	ID       int64    `json:"id"`
   472  	Name     CIStr    `json:"name"`
   473  	LessThan []string `json:"less_than"`
   474  	Comment  string   `json:"comment,omitempty"`
   475  }
   476  
   477  // IndexColumn provides index column info.
   478  type IndexColumn struct {
   479  	Name   CIStr `json:"name"`   // Index name
   480  	Offset int   `json:"offset"` // Index offset
   481  	// Length of prefix when using column prefix
   482  	// for indexing;
   483  	// UnspecifedLength if not using prefix indexing
   484  	Length int `json:"length"`
   485  }
   486  
   487  // Clone clones IndexColumn.
   488  func (i *IndexColumn) Clone() *IndexColumn {
   489  	ni := *i
   490  	return &ni
   491  }
   492  
   493  // IndexType is the type of index
   494  type IndexType int
   495  
   496  // String implements Stringer interface.
   497  func (t IndexType) String() string {
   498  	switch t {
   499  	case IndexTypeBtree:
   500  		return "BTREE"
   501  	case IndexTypeHash:
   502  		return "HASH"
   503  	default:
   504  		return ""
   505  	}
   506  }
   507  
   508  // IndexTypes
   509  const (
   510  	IndexTypeInvalid IndexType = iota
   511  	IndexTypeBtree
   512  	IndexTypeHash
   513  )
   514  
   515  // IndexInfo provides meta data describing a DB index.
   516  // It corresponds to the statement `CREATE INDEX Name ON Table (Column);`
   517  // See https://dev.mysql.com/doc/refman/5.7/en/create-index.html
   518  type IndexInfo struct {
   519  	ID      int64          `json:"id"`
   520  	Name    CIStr          `json:"idx_name"`   // Index name.
   521  	Table   CIStr          `json:"tbl_name"`   // Table name.
   522  	Columns []*IndexColumn `json:"idx_cols"`   // Index columns.
   523  	Unique  bool           `json:"is_unique"`  // Whether the index is unique.
   524  	Primary bool           `json:"is_primary"` // Whether the index is primary key.
   525  	State   SchemaState    `json:"state"`
   526  	Comment string         `json:"comment"`    // Comment
   527  	Tp      IndexType      `json:"index_type"` // Index type: Btree or Hash
   528  }
   529  
   530  // Clone clones IndexInfo.
   531  func (index *IndexInfo) Clone() *IndexInfo {
   532  	ni := *index
   533  	ni.Columns = make([]*IndexColumn, len(index.Columns))
   534  	for i := range index.Columns {
   535  		ni.Columns[i] = index.Columns[i].Clone()
   536  	}
   537  	return &ni
   538  }
   539  
   540  // HasPrefixIndex returns whether any columns of this index uses prefix length.
   541  func (index *IndexInfo) HasPrefixIndex() bool {
   542  	for _, ic := range index.Columns {
   543  		if ic.Length != types.UnspecifiedLength {
   544  			return true
   545  		}
   546  	}
   547  	return false
   548  }
   549  
   550  // FKInfo provides meta data describing a foreign key constraint.
   551  type FKInfo struct {
   552  	ID       int64       `json:"id"`
   553  	Name     CIStr       `json:"fk_name"`
   554  	RefTable CIStr       `json:"ref_table"`
   555  	RefCols  []CIStr     `json:"ref_cols"`
   556  	Cols     []CIStr     `json:"cols"`
   557  	OnDelete int         `json:"on_delete"`
   558  	OnUpdate int         `json:"on_update"`
   559  	State    SchemaState `json:"state"`
   560  }
   561  
   562  // Clone clones FKInfo.
   563  func (fk *FKInfo) Clone() *FKInfo {
   564  	nfk := *fk
   565  
   566  	nfk.RefCols = make([]CIStr, len(fk.RefCols))
   567  	nfk.Cols = make([]CIStr, len(fk.Cols))
   568  	copy(nfk.RefCols, fk.RefCols)
   569  	copy(nfk.Cols, fk.Cols)
   570  
   571  	return &nfk
   572  }
   573  
   574  // DBInfo provides meta data describing a DB.
   575  type DBInfo struct {
   576  	ID      int64        `json:"id"`      // Database ID
   577  	Name    CIStr        `json:"db_name"` // DB name.
   578  	Charset string       `json:"charset"`
   579  	Collate string       `json:"collate"`
   580  	Tables  []*TableInfo `json:"-"` // Tables in the DB.
   581  	State   SchemaState  `json:"state"`
   582  }
   583  
   584  // Clone clones DBInfo.
   585  func (db *DBInfo) Clone() *DBInfo {
   586  	newInfo := *db
   587  	newInfo.Tables = make([]*TableInfo, len(db.Tables))
   588  	for i := range db.Tables {
   589  		newInfo.Tables[i] = db.Tables[i].Clone()
   590  	}
   591  	return &newInfo
   592  }
   593  
   594  // Copy shallow copies DBInfo.
   595  func (db *DBInfo) Copy() *DBInfo {
   596  	newInfo := *db
   597  	newInfo.Tables = make([]*TableInfo, len(db.Tables))
   598  	copy(newInfo.Tables, db.Tables)
   599  	return &newInfo
   600  }
   601  
   602  // CIStr is case insensitive string.
   603  type CIStr struct {
   604  	O string `json:"O"` // Original string.
   605  	L string `json:"L"` // Lower case string.
   606  }
   607  
   608  // String implements fmt.Stringer interface.
   609  func (cis CIStr) String() string {
   610  	return cis.O
   611  }
   612  
   613  // NewCIStr creates a new CIStr.
   614  func NewCIStr(s string) (cs CIStr) {
   615  	cs.O = s
   616  	cs.L = strings.ToLower(s)
   617  	return
   618  }
   619  
   620  // UnmarshalJSON implements the user defined unmarshal method.
   621  // CIStr can be unmarshaled from a single string, so PartitionDefinition.Name
   622  // in this change https://github.com/pingcap/tidb/pull/6460/files would be
   623  // compatible during TiDB upgrading.
   624  func (cis *CIStr) UnmarshalJSON(b []byte) error {
   625  	type T CIStr
   626  	if err := json.Unmarshal(b, (*T)(cis)); err == nil {
   627  		return nil
   628  	}
   629  
   630  	// Unmarshal CIStr from a single string.
   631  	err := json.Unmarshal(b, &cis.O)
   632  	if err != nil {
   633  		return errors.Trace(err)
   634  	}
   635  	cis.L = strings.ToLower(cis.O)
   636  	return nil
   637  }
   638  
   639  // ColumnsToProto converts a slice of model.ColumnInfo to a slice of tipb.ColumnInfo.
   640  func ColumnsToProto(columns []*ColumnInfo, pkIsHandle bool) []*tipb.ColumnInfo {
   641  	cols := make([]*tipb.ColumnInfo, 0, len(columns))
   642  	for _, c := range columns {
   643  		col := ColumnToProto(c)
   644  		// TODO: Here `PkHandle`'s meaning is changed, we will change it to `IsHandle` when tikv's old select logic
   645  		// is abandoned.
   646  		if (pkIsHandle && mysql.HasPriKeyFlag(c.Flag)) || c.ID == ExtraHandleID {
   647  			col.PkHandle = true
   648  		} else {
   649  			col.PkHandle = false
   650  		}
   651  		cols = append(cols, col)
   652  	}
   653  	return cols
   654  }
   655  
   656  // IndexToProto converts a model.IndexInfo to a tipb.IndexInfo.
   657  func IndexToProto(t *TableInfo, idx *IndexInfo) *tipb.IndexInfo {
   658  	pi := &tipb.IndexInfo{
   659  		TableId: t.ID,
   660  		IndexId: idx.ID,
   661  		Unique:  idx.Unique,
   662  	}
   663  	cols := make([]*tipb.ColumnInfo, 0, len(idx.Columns)+1)
   664  	for _, c := range idx.Columns {
   665  		cols = append(cols, ColumnToProto(t.Columns[c.Offset]))
   666  	}
   667  	if t.PKIsHandle {
   668  		// Coprocessor needs to know PKHandle column info, so we need to append it.
   669  		for _, col := range t.Columns {
   670  			if mysql.HasPriKeyFlag(col.Flag) {
   671  				colPB := ColumnToProto(col)
   672  				colPB.PkHandle = true
   673  				cols = append(cols, colPB)
   674  				break
   675  			}
   676  		}
   677  	}
   678  	pi.Columns = cols
   679  	return pi
   680  }
   681  
   682  // ColumnToProto converts model.ColumnInfo to tipb.ColumnInfo.
   683  func ColumnToProto(c *ColumnInfo) *tipb.ColumnInfo {
   684  	pc := &tipb.ColumnInfo{
   685  		ColumnId:  c.ID,
   686  		Collation: collationToProto(c.FieldType.Collate),
   687  		ColumnLen: int32(c.FieldType.Flen),
   688  		Decimal:   int32(c.FieldType.Decimal),
   689  		Flag:      int32(c.Flag),
   690  		Elems:     c.Elems,
   691  	}
   692  	pc.Tp = int32(c.FieldType.Tp)
   693  	return pc
   694  }
   695  
   696  // TODO: update it when more collate is supported.
   697  func collationToProto(c string) int32 {
   698  	v := mysql.CollationNames[c]
   699  	if v == mysql.BinaryCollationID {
   700  		return int32(mysql.BinaryCollationID)
   701  	}
   702  	// We only support binary and utf8_bin collation.
   703  	// Setting other collations to utf8_bin for old data compatibility.
   704  	// For the data created when we didn't enforce utf8_bin collation in create table.
   705  	return int32(mysql.DefaultCollationID)
   706  }
   707  
   708  // TableColumnID is composed by table ID and column ID.
   709  type TableColumnID struct {
   710  	TableID  int64
   711  	ColumnID int64
   712  }