github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/alter_foreign_key.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  	"sort"
    20  	"strings"
    21  
    22  	"github.com/dolthub/vitess/go/sqltypes"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  	"github.com/dolthub/go-mysql-server/sql/types"
    26  )
    27  
    28  type CreateForeignKey struct {
    29  	// In the cases where we have multiple ALTER statements, we need to resolve the table at execution time rather than
    30  	// during analysis. Otherwise, you could add a column in the preceding alter and we may have analyzed to a table
    31  	// that did not yet have that column.
    32  	DbProvider sql.DatabaseProvider
    33  	FkDef      *sql.ForeignKeyConstraint
    34  }
    35  
    36  var _ sql.Node = (*CreateForeignKey)(nil)
    37  var _ sql.MultiDatabaser = (*CreateForeignKey)(nil)
    38  var _ sql.Databaseable = (*CreateForeignKey)(nil)
    39  var _ sql.CollationCoercible = (*CreateForeignKey)(nil)
    40  
    41  func NewAlterAddForeignKey(fkDef *sql.ForeignKeyConstraint) *CreateForeignKey {
    42  	return &CreateForeignKey{
    43  		DbProvider: nil,
    44  		FkDef:      fkDef,
    45  	}
    46  }
    47  
    48  func (p *CreateForeignKey) Database() string {
    49  	return p.FkDef.Database
    50  }
    51  
    52  // Resolved implements the interface sql.Node.
    53  func (p *CreateForeignKey) Resolved() bool {
    54  	return p.DbProvider != nil
    55  }
    56  
    57  func (p *CreateForeignKey) IsReadOnly() bool {
    58  	return false
    59  }
    60  
    61  // Children implements the interface sql.Node.
    62  func (p *CreateForeignKey) Children() []sql.Node {
    63  	return nil
    64  }
    65  
    66  // WithChildren implements the interface sql.Node.
    67  func (p *CreateForeignKey) WithChildren(children ...sql.Node) (sql.Node, error) {
    68  	return NillaryWithChildren(p, children...)
    69  }
    70  
    71  // CheckPrivileges implements the interface sql.Node.
    72  func (p *CreateForeignKey) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
    73  	subject := sql.PrivilegeCheckSubject{
    74  		Database: p.FkDef.ParentDatabase,
    75  		Table:    p.FkDef.ParentTable,
    76  	}
    77  
    78  	return opChecker.UserHasPrivileges(ctx,
    79  		sql.NewPrivilegedOperation(subject, sql.PrivilegeType_References))
    80  }
    81  
    82  // CollationCoercibility implements the interface sql.CollationCoercible.
    83  func (*CreateForeignKey) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
    84  	return sql.Collation_binary, 7
    85  }
    86  
    87  // Schema implements the interface sql.Node.
    88  func (p *CreateForeignKey) Schema() sql.Schema {
    89  	return types.OkResultSchema
    90  }
    91  
    92  // DatabaseProvider implements the interface sql.MultiDatabaser.
    93  func (p *CreateForeignKey) DatabaseProvider() sql.DatabaseProvider {
    94  	return p.DbProvider
    95  }
    96  
    97  // WithDatabaseProvider implements the interface sql.MultiDatabaser.
    98  func (p *CreateForeignKey) WithDatabaseProvider(provider sql.DatabaseProvider) (sql.Node, error) {
    99  	np := *p
   100  	np.DbProvider = provider
   101  	return &np, nil
   102  }
   103  
   104  // String implements the interface sql.Node.
   105  func (p *CreateForeignKey) String() string {
   106  	pr := sql.NewTreePrinter()
   107  	_ = pr.WriteNode("AddForeignKey(%s)", p.FkDef.Name)
   108  	_ = pr.WriteChildren(
   109  		fmt.Sprintf("Table(%s.%s)", p.FkDef.Database, p.FkDef.Table),
   110  		fmt.Sprintf("Columns(%s)", strings.Join(p.FkDef.Columns, ", ")),
   111  		fmt.Sprintf("ParentTable(%s.%s)", p.FkDef.ParentDatabase, p.FkDef.ParentTable),
   112  		fmt.Sprintf("ParentColumns(%s)", strings.Join(p.FkDef.ParentColumns, ", ")),
   113  		fmt.Sprintf("OnUpdate(%s)", p.FkDef.OnUpdate),
   114  		fmt.Sprintf("OnDelete(%s)", p.FkDef.OnDelete))
   115  	return pr.String()
   116  }
   117  
   118  // ResolveForeignKey verifies the foreign key definition and resolves the foreign key, creating indexes and validating
   119  // data as necessary.
   120  // fkChecks - whether to check the foreign key against the data in the table
   121  // checkRows - whether to check the existing rows in the table against the foreign key
   122  func ResolveForeignKey(ctx *sql.Context, tbl sql.ForeignKeyTable, refTbl sql.ForeignKeyTable, fkDef sql.ForeignKeyConstraint, shouldAdd, fkChecks, checkRows bool) error {
   123  	if t, ok := tbl.(sql.TemporaryTable); ok && t.IsTemporary() {
   124  		return sql.ErrTemporaryTablesForeignKeySupport.New()
   125  	}
   126  	if fkDef.IsResolved {
   127  		return fmt.Errorf("cannot resolve foreign key `%s` as it has already been resolved", fkDef.Name)
   128  	}
   129  	if len(fkDef.Columns) == 0 {
   130  		return sql.ErrForeignKeyMissingColumns.New()
   131  	}
   132  	if len(fkDef.Columns) != len(fkDef.ParentColumns) {
   133  		return sql.ErrForeignKeyColumnCountMismatch.New()
   134  	}
   135  
   136  	// Make sure that all columns are valid, in the table, and there are no duplicates
   137  	cols := make(map[string]*sql.Column)
   138  	seenCols := make(map[string]struct{})
   139  	for _, col := range tbl.Schema() {
   140  		lowerColName := strings.ToLower(col.Name)
   141  		cols[lowerColName] = col
   142  	}
   143  	for i, fkCol := range fkDef.Columns {
   144  		lowerFkCol := strings.ToLower(fkCol)
   145  		col, ok := cols[lowerFkCol]
   146  		if !ok {
   147  			return sql.ErrTableColumnNotFound.New(tbl.Name(), fkCol)
   148  		}
   149  		_, ok = seenCols[lowerFkCol]
   150  		if ok {
   151  			return sql.ErrAddForeignKeyDuplicateColumn.New(fkCol)
   152  		}
   153  		// Non-nullable columns may not have SET NULL as a reference option
   154  		if !col.Nullable && (fkDef.OnUpdate == sql.ForeignKeyReferentialAction_SetNull || fkDef.OnDelete == sql.ForeignKeyReferentialAction_SetNull) {
   155  			return sql.ErrForeignKeySetNullNonNullable.New(col.Name)
   156  		}
   157  		seenCols[lowerFkCol] = struct{}{}
   158  		fkDef.Columns[i] = col.Name
   159  	}
   160  
   161  	// Do the same for the referenced columns
   162  	if fkChecks {
   163  		parentCols := make(map[string]*sql.Column)
   164  		seenCols = make(map[string]struct{})
   165  		for _, col := range refTbl.Schema() {
   166  			lowerColName := strings.ToLower(col.Name)
   167  			parentCols[lowerColName] = col
   168  		}
   169  		for i, fkParentCol := range fkDef.ParentColumns {
   170  			lowerFkParentCol := strings.ToLower(fkParentCol)
   171  			parentCol, ok := parentCols[lowerFkParentCol]
   172  			if !ok {
   173  				return sql.ErrTableColumnNotFound.New(fkDef.ParentTable, fkParentCol)
   174  			}
   175  			_, ok = seenCols[lowerFkParentCol]
   176  			if ok {
   177  				return sql.ErrAddForeignKeyDuplicateColumn.New(fkParentCol)
   178  			}
   179  			seenCols[lowerFkParentCol] = struct{}{}
   180  			fkDef.ParentColumns[i] = parentCol.Name
   181  		}
   182  
   183  		// Check that the types align and are valid
   184  		for i := range fkDef.Columns {
   185  			col := cols[strings.ToLower(fkDef.Columns[i])]
   186  			parentCol := parentCols[strings.ToLower(fkDef.ParentColumns[i])]
   187  			if !foreignKeyComparableTypes(ctx, col.Type, parentCol.Type) {
   188  				return sql.ErrForeignKeyColumnTypeMismatch.New(fkDef.Columns[i], fkDef.ParentColumns[i])
   189  			}
   190  			sqlParserType := col.Type.Type()
   191  			if sqlParserType == sqltypes.Text || sqlParserType == sqltypes.Blob {
   192  				return sql.ErrForeignKeyTextBlob.New()
   193  			}
   194  		}
   195  
   196  		// Ensure that a suitable index exists on the referenced table, and check the declaring table for a suitable index.
   197  		refTblIndex, ok, err := FindFKIndexWithPrefix(ctx, refTbl, fkDef.ParentColumns, true)
   198  		if err != nil {
   199  			return err
   200  		}
   201  		if !ok {
   202  			return sql.ErrForeignKeyMissingReferenceIndex.New(fkDef.Name, fkDef.ParentTable)
   203  		}
   204  
   205  		indexPositions, appendTypes, err := FindForeignKeyColMapping(ctx, fkDef.Name, tbl, fkDef.Columns, fkDef.ParentColumns, refTblIndex)
   206  		if err != nil {
   207  			return err
   208  		}
   209  		var selfCols map[string]int
   210  		if fkDef.IsSelfReferential() {
   211  			selfCols = make(map[string]int)
   212  			for i, col := range tbl.Schema() {
   213  				selfCols[strings.ToLower(col.Name)] = i
   214  			}
   215  		}
   216  		reference := &ForeignKeyReferenceHandler{
   217  			ForeignKey: fkDef,
   218  			SelfCols:   selfCols,
   219  			RowMapper: ForeignKeyRowMapper{
   220  				Index:          refTblIndex,
   221  				Updater:        refTbl.GetForeignKeyEditor(ctx),
   222  				SourceSch:      tbl.Schema(),
   223  				IndexPositions: indexPositions,
   224  				AppendTypes:    appendTypes,
   225  			},
   226  		}
   227  
   228  		if checkRows {
   229  			if err := reference.CheckTable(ctx, tbl); err != nil {
   230  				return err
   231  			}
   232  		}
   233  	}
   234  
   235  	// Check if the current foreign key name has already been used. Rather than checking the table first (which is the
   236  	// highest cost part of creating a foreign key), we'll check the name if it needs to be checked. If the foreign key
   237  	// was previously added, we don't need to check the name.
   238  	if shouldAdd {
   239  		existingFks, err := tbl.GetDeclaredForeignKeys(ctx)
   240  		if err != nil {
   241  			return err
   242  		}
   243  		fkLowerName := strings.ToLower(fkDef.Name)
   244  		for _, existingFk := range existingFks {
   245  			if fkLowerName == strings.ToLower(existingFk.Name) {
   246  				return sql.ErrForeignKeyDuplicateName.New(fkDef.Name)
   247  			}
   248  		}
   249  	}
   250  
   251  	_, ok, err := FindFKIndexWithPrefix(ctx, tbl, fkDef.Columns, false)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	if !ok {
   256  		indexColumns := make([]sql.IndexColumn, len(fkDef.Columns))
   257  		for i, col := range fkDef.Columns {
   258  			indexColumns[i] = sql.IndexColumn{
   259  				Name:   col,
   260  				Length: 0,
   261  			}
   262  		}
   263  		indexMap := make(map[string]struct{})
   264  		indexes, err := tbl.GetIndexes(ctx)
   265  		if err != nil {
   266  			return err
   267  		}
   268  		for _, index := range indexes {
   269  			indexMap[strings.ToLower(index.ID())] = struct{}{}
   270  		}
   271  		indexName := strings.Join(fkDef.Columns, "")
   272  		if _, ok = indexMap[strings.ToLower(indexName)]; ok {
   273  			for i := 0; true; i++ {
   274  				newIndexName := fmt.Sprintf("%s_%d", indexName, i)
   275  				if _, ok = indexMap[strings.ToLower(newIndexName)]; !ok {
   276  					indexName = newIndexName
   277  					break
   278  				}
   279  			}
   280  		}
   281  		err = tbl.CreateIndexForForeignKey(ctx, sql.IndexDef{
   282  			Name:       indexName,
   283  			Columns:    indexColumns,
   284  			Constraint: sql.IndexConstraint_None,
   285  			Storage:    sql.IndexUsing_Default,
   286  		})
   287  		if err != nil {
   288  			return err
   289  		}
   290  	}
   291  
   292  	if shouldAdd {
   293  		fkDef.IsResolved = fkChecks
   294  		return tbl.AddForeignKey(ctx, fkDef)
   295  	} else {
   296  		fkDef.IsResolved = fkChecks
   297  		return tbl.UpdateForeignKey(ctx, fkDef.Name, fkDef)
   298  	}
   299  }
   300  
   301  type DropForeignKey struct {
   302  	// In the cases where we have multiple ALTER statements, we need to resolve the table at execution time rather than
   303  	// during analysis. Otherwise, you could add a foreign key in the preceding alter and we may have analyzed to a
   304  	// table that did not yet have that foreign key.
   305  	DbProvider sql.DatabaseProvider
   306  	database   string
   307  	Table      string
   308  	Name       string
   309  }
   310  
   311  var _ sql.Node = (*DropForeignKey)(nil)
   312  var _ sql.MultiDatabaser = (*DropForeignKey)(nil)
   313  var _ sql.Databaseable = (*DropForeignKey)(nil)
   314  var _ sql.CollationCoercible = (*DropForeignKey)(nil)
   315  
   316  func NewAlterDropForeignKey(db, table, name string) *DropForeignKey {
   317  	return &DropForeignKey{
   318  		DbProvider: nil,
   319  		database:   db,
   320  		Table:      table,
   321  		Name:       name,
   322  	}
   323  }
   324  
   325  func (p *DropForeignKey) Database() string {
   326  	return p.database
   327  }
   328  
   329  // WithChildren implements the interface sql.Node.
   330  func (p *DropForeignKey) WithChildren(children ...sql.Node) (sql.Node, error) {
   331  	return NillaryWithChildren(p, children...)
   332  }
   333  
   334  // CheckPrivileges implements the interface sql.Node.
   335  func (p *DropForeignKey) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   336  	subject := sql.PrivilegeCheckSubject{
   337  		Database: p.database,
   338  		Table:    p.Table,
   339  	}
   340  	return opChecker.UserHasPrivileges(ctx,
   341  		sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Alter))
   342  }
   343  
   344  // CollationCoercibility implements the interface sql.CollationCoercible.
   345  func (*DropForeignKey) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   346  	return sql.Collation_binary, 7
   347  }
   348  
   349  // Schema implements the interface sql.Node.
   350  func (p *DropForeignKey) Schema() sql.Schema {
   351  	return types.OkResultSchema
   352  }
   353  
   354  // DatabaseProvider implements the interface sql.MultiDatabaser.
   355  func (p *DropForeignKey) DatabaseProvider() sql.DatabaseProvider {
   356  	return p.DbProvider
   357  }
   358  
   359  // WithDatabaseProvider implements the interface sql.MultiDatabaser.
   360  func (p *DropForeignKey) WithDatabaseProvider(provider sql.DatabaseProvider) (sql.Node, error) {
   361  	np := *p
   362  	np.DbProvider = provider
   363  	return &np, nil
   364  }
   365  
   366  // Resolved implements the interface sql.Node.
   367  func (p *DropForeignKey) Resolved() bool {
   368  	return p.DbProvider != nil
   369  }
   370  
   371  func (p *DropForeignKey) IsReadOnly() bool {
   372  	return false
   373  }
   374  
   375  // Children implements the interface sql.Node.
   376  func (p *DropForeignKey) Children() []sql.Node {
   377  	return nil
   378  }
   379  
   380  // String implements the interface sql.Node.
   381  func (p *DropForeignKey) String() string {
   382  	pr := sql.NewTreePrinter()
   383  	_ = pr.WriteNode("DropForeignKey(%s)", p.Name)
   384  	_ = pr.WriteChildren(fmt.Sprintf("Table(%s.%s)", p.Database(), p.Table))
   385  	return pr.String()
   386  }
   387  
   388  // FindForeignKeyColMapping returns the mapping from a given row to its equivalent index position, based on the matching
   389  // foreign key columns. This also verifies that the column types match, as it is a prerequisite for mapping. For foreign
   390  // keys that do not match the full index, also returns the types to append during the key mapping, as all index columns
   391  // must have a column expression. All strings are case-insensitive.
   392  func FindForeignKeyColMapping(
   393  	ctx *sql.Context,
   394  	fkName string,
   395  	localTbl sql.ForeignKeyTable,
   396  	localFKCols []string,
   397  	destFKCols []string,
   398  	index sql.Index,
   399  ) ([]int, []sql.Type, error) {
   400  	localFKCols = lowercaseSlice(localFKCols)
   401  	destFKCols = lowercaseSlice(destFKCols)
   402  	destTblName := strings.ToLower(index.Table())
   403  
   404  	localSchTypeMap := make(map[string]sql.Type)
   405  	localSchPositionMap := make(map[string]int)
   406  	for i, col := range localTbl.Schema() {
   407  		colName := strings.ToLower(col.Name)
   408  		localSchTypeMap[colName] = col.Type
   409  		localSchPositionMap[colName] = i
   410  	}
   411  	var appendTypes []sql.Type
   412  	indexTypeMap := make(map[string]sql.Type)
   413  	indexColMap := make(map[string]int)
   414  	var columnExpressionTypes []sql.ColumnExpressionType
   415  	if extendedIndex, ok := index.(sql.ExtendedIndex); ok {
   416  		columnExpressionTypes = extendedIndex.ExtendedColumnExpressionTypes()
   417  	} else {
   418  		columnExpressionTypes = index.ColumnExpressionTypes()
   419  	}
   420  	for i, indexCol := range columnExpressionTypes {
   421  		indexColName := strings.ToLower(indexCol.Expression)
   422  		indexTypeMap[indexColName] = indexCol.Type
   423  		indexColMap[indexColName] = i
   424  		if i >= len(destFKCols) {
   425  			appendTypes = append(appendTypes, indexCol.Type)
   426  		}
   427  	}
   428  	indexPositions := make([]int, len(destFKCols))
   429  
   430  	for fkIdx, colName := range localFKCols {
   431  		localRowPos, ok := localSchPositionMap[colName]
   432  		if !ok {
   433  			// Will happen if a column is renamed that is referenced by a foreign key
   434  			//TODO: enforce that renaming a column referenced by a foreign key updates that foreign key
   435  			return nil, nil, fmt.Errorf("column `%s` in foreign key `%s` cannot be found",
   436  				colName, fkName)
   437  		}
   438  		expectedType := localSchTypeMap[colName]
   439  		destFkCol := destTblName + "." + destFKCols[fkIdx]
   440  		indexPos, ok := indexColMap[destFkCol]
   441  		if !ok {
   442  			// Same as above, renaming a referenced column would cause this error
   443  			return nil, nil, fmt.Errorf("index column `%s` in foreign key `%s` cannot be found",
   444  				destFKCols[fkIdx], fkName)
   445  		}
   446  		if !foreignKeyComparableTypes(ctx, indexTypeMap[destFkCol], expectedType) {
   447  			return nil, nil, sql.ErrForeignKeyColumnTypeMismatch.New(colName, destFkCol)
   448  		}
   449  		indexPositions[indexPos] = localRowPos
   450  	}
   451  	return indexPositions, appendTypes, nil
   452  }
   453  
   454  // FindFKIndexWithPrefix returns an index that has the given columns as a prefix, with the index intended for use with
   455  // foreign keys. The returned index is deterministic and follows the given rules, from the highest priority in descending order:
   456  //
   457  // 1. Columns exactly match the index
   458  // 2. Columns match as much of the index prefix as possible
   459  // 3. Unique index before non-unique
   460  // 4. Largest index by column count
   461  // 5. Index ID in ascending order
   462  //
   463  // The prefix columns may be in any order, and the returned index will contain all of the prefix columns within its
   464  // prefix. For example, the slices [col1, col2] and [col2, col1] will match the same index, as their ordering does not
   465  // matter. The index [col1, col2, col3] would match, but the index [col1, col3] would not match as it is missing "col2".
   466  // Prefix columns are case-insensitive.
   467  //
   468  // If `useExtendedIndexes` is true, then this will include any implicit primary keys that were not explicitly defined on
   469  // the index. Some operations only consider explicitly indexed columns, while others also consider any implicit primary
   470  // keys as well, therefore this is a boolean to control the desired behavior.
   471  func FindFKIndexWithPrefix(ctx *sql.Context, tbl sql.IndexAddressableTable, prefixCols []string, useExtendedIndexes bool, ignoredIndexes ...string) (sql.Index, bool, error) {
   472  	type idxWithLen struct {
   473  		sql.Index
   474  		colLen int
   475  	}
   476  
   477  	ignoredIndexesMap := make(map[string]struct{})
   478  	for _, ignoredIndex := range ignoredIndexes {
   479  		ignoredIndexesMap[strings.ToLower(ignoredIndex)] = struct{}{}
   480  	}
   481  
   482  	indexes, err := tbl.GetIndexes(ctx)
   483  	if err != nil {
   484  		return nil, false, err
   485  	}
   486  	// Ignore indexes with prefix lengths; they are unsupported in MySQL
   487  	// https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html#:~:text=Index%20prefixes%20on%20foreign%20key%20columns%20are%20not%20supported.
   488  	// Ignore spatial indexes; MySQL will not pick them as the underlying secondary index for foreign keys
   489  	for _, idx := range indexes {
   490  		if len(idx.PrefixLengths()) > 0 || idx.IsSpatial() || idx.IsFullText() {
   491  			ignoredIndexesMap[strings.ToLower(idx.ID())] = struct{}{}
   492  		}
   493  	}
   494  	tblName := strings.ToLower(tbl.Name())
   495  	exprCols := make([]string, len(prefixCols))
   496  	for i, prefixCol := range prefixCols {
   497  		exprCols[i] = tblName + "." + strings.ToLower(prefixCol)
   498  	}
   499  	colLen := len(exprCols)
   500  	var indexesWithLen []idxWithLen
   501  	for _, idx := range indexes {
   502  		if _, ok := ignoredIndexesMap[strings.ToLower(idx.ID())]; ok {
   503  			continue
   504  		}
   505  		var indexExprs []string
   506  		if extendedIdx, ok := idx.(sql.ExtendedIndex); ok && useExtendedIndexes {
   507  			indexExprs = lowercaseSlice(extendedIdx.ExtendedExpressions())
   508  		} else {
   509  			indexExprs = lowercaseSlice(idx.Expressions())
   510  		}
   511  		if ok := exprsAreIndexPrefix(exprCols, indexExprs); ok {
   512  			indexesWithLen = append(indexesWithLen, idxWithLen{idx, len(indexExprs)})
   513  		}
   514  	}
   515  	if len(indexesWithLen) == 0 {
   516  		return nil, false, nil
   517  	}
   518  
   519  	sort.Slice(indexesWithLen, func(i, j int) bool {
   520  		idxI := indexesWithLen[i]
   521  		idxJ := indexesWithLen[j]
   522  		if idxI.colLen == colLen && idxJ.colLen != colLen {
   523  			return true
   524  		} else if idxI.colLen != colLen && idxJ.colLen == colLen {
   525  			return false
   526  		} else if idxI.Index.IsUnique() != idxJ.Index.IsUnique() {
   527  			return idxI.Index.IsUnique()
   528  		} else if idxI.colLen != idxJ.colLen {
   529  			return idxI.colLen > idxJ.colLen
   530  		} else {
   531  			return idxI.Index.ID() < idxJ.Index.ID()
   532  		}
   533  	})
   534  	sortedIndexes := make([]sql.Index, len(indexesWithLen))
   535  	for i := 0; i < len(sortedIndexes); i++ {
   536  		sortedIndexes[i] = indexesWithLen[i].Index
   537  	}
   538  	return sortedIndexes[0], true, nil
   539  }
   540  
   541  // foreignKeyComparableTypes returns whether the two given types are able to be used as parent/child columns in a
   542  // foreign key.
   543  func foreignKeyComparableTypes(ctx *sql.Context, type1 sql.Type, type2 sql.Type) bool {
   544  	if !type1.Equals(type2) {
   545  		// There seems to be a special case where CHAR/VARCHAR/BINARY/VARBINARY can have unequal lengths.
   546  		// Have not tested every type nor combination, but this seems specific to those 4 types.
   547  		if type1.Type() == type2.Type() {
   548  			switch type1.Type() {
   549  			case sqltypes.Char, sqltypes.VarChar, sqltypes.Binary, sqltypes.VarBinary:
   550  				type1String := type1.(sql.StringType)
   551  				type2String := type2.(sql.StringType)
   552  				if type1String.Collation().CharacterSet() != type2String.Collation().CharacterSet() {
   553  					return false
   554  				}
   555  			default:
   556  				return false
   557  			}
   558  		} else {
   559  			return false
   560  		}
   561  	}
   562  	return true
   563  }
   564  
   565  // exprsAreIndexPrefix returns whether the given expressions are a prefix of the given index expressions
   566  func exprsAreIndexPrefix(exprs, indexExprs []string) bool {
   567  	if len(exprs) > len(indexExprs) {
   568  		return false
   569  	}
   570  
   571  	for i := 0; i < len(exprs); i++ {
   572  		if exprs[i] != indexExprs[i] {
   573  			return false
   574  		}
   575  	}
   576  
   577  	return true
   578  }
   579  
   580  func lowercaseSlice(strs []string) []string {
   581  	newStrs := make([]string, len(strs))
   582  	for i, str := range strs {
   583  		newStrs[i] = strings.ToLower(str)
   584  	}
   585  	return newStrs
   586  }