Skip to content

Commit 9c85746

Browse files
committed
Stub for converting types when doing foreign key checks
1 parent 575b04b commit 9c85746

File tree

4 files changed

+80
-6
lines changed

4 files changed

+80
-6
lines changed

sql/analyzer/apply_foreign_keys.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,11 @@ func getForeignKeyRefActions(ctx *sql.Context, a *Analyzer, tbl sql.ForeignKeyTa
379379
return nil, err
380380
}
381381

382+
typeTransforms, err := plan.GetChildParentTypeTransforms(tblSch, childTblSch, fk)
383+
if err != nil {
384+
return nil, err
385+
}
386+
382387
childEditor, err := getForeignKeyEditor(ctx, a, childTbl, cache, fkChain.AddForeignKey(fk.Name), checkRows)
383388
if err != nil {
384389
return nil, err
@@ -402,11 +407,12 @@ func getForeignKeyRefActions(ctx *sql.Context, a *Analyzer, tbl sql.ForeignKeyTa
402407
}
403408
fkEditor.RefActions[i] = plan.ForeignKeyRefActionData{
404409
RowMapper: &plan.ForeignKeyRowMapper{
405-
Index: childIndex,
406-
Updater: childUpdater,
407-
SourceSch: tblSch,
408-
IndexPositions: indexPositions,
409-
AppendTypes: appendTypes,
410+
Index: childIndex,
411+
Updater: childUpdater,
412+
SourceSch: tblSch,
413+
TargetTypeTransforms: typeTransforms,
414+
IndexPositions: indexPositions,
415+
AppendTypes: appendTypes,
410416
},
411417
Editor: childEditor,
412418
ForeignKey: fk,

sql/plan/foreign_key_editor.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"strings"
2121

2222
"github.com/dolthub/go-mysql-server/sql"
23+
"github.com/dolthub/go-mysql-server/sql/types"
2324
)
2425

2526
// ChildParentMapping is a mapping from the foreign key columns of a child schema to the parent schema. The position
@@ -455,6 +456,9 @@ type ForeignKeyRowMapper struct {
455456
Index sql.Index
456457
Updater sql.ForeignKeyEditor
457458
SourceSch sql.Schema
459+
// TargetTypeTransforms are a set of functions to transform the value in the table to the corresponding value in the
460+
// other table. This is required when the types of the two tables are compatible but different (e.g. INT and BIGINT).
461+
TargetTypeTransforms []func(ctx *sql.Context, val any) (any, error)
458462
// IndexPositions hold the mapping between an index's column position and the source row's column position. Given
459463
// an index (x1, x2) and a source row (y1, y2, y3) and the relation (x1->y3, x2->y1), this slice would contain
460464
// [2, 0]. The first index column "x1" maps to the third source column "y3" (so position 2 since it's zero-based),
@@ -481,6 +485,17 @@ func (mapper *ForeignKeyRowMapper) GetIter(ctx *sql.Context, row sql.Row, refChe
481485
if rowVal == nil {
482486
return sql.RowsToRowIter(), nil
483487
}
488+
489+
// Transform the type of the value in this row to the one in the other table for the index lookup, if necessary
490+
if mapper.TargetTypeTransforms != nil && mapper.TargetTypeTransforms[rowPos] != nil {
491+
var err error
492+
rowVal, err = mapper.TargetTypeTransforms[rowPos](ctx, rowVal)
493+
// TODO: possible for this to fail without error, which means the value cannot be found in the other table
494+
if err != nil {
495+
return nil, err
496+
}
497+
}
498+
484499
rang[rangPosition] = sql.ClosedRangeColumnExpr(rowVal, rowVal, mapper.SourceSch[rowPos].Type)
485500
}
486501
for i, appendType := range mapper.AppendTypes {
@@ -547,3 +562,53 @@ func GetChildParentMapping(parentSch sql.Schema, childSch sql.Schema, fkDef sql.
547562
}
548563
return mapping, nil
549564
}
565+
566+
// GetChildParentTypeTransforms returs a set of functions to transform the value in the child table to the
567+
// corresponding type in the parent table, if necessary
568+
func GetChildParentTypeTransforms(parentSch sql.Schema, childSch sql.Schema, fkDef sql.ForeignKeyConstraint) ([]func(ctx *sql.Context, val any) (any, error), error) {
569+
570+
parentMap := make(map[string]int)
571+
for i, col := range parentSch {
572+
parentMap[strings.ToLower(col.Name)] = i
573+
}
574+
childMap := make(map[string]int)
575+
for i, col := range childSch {
576+
childMap[strings.ToLower(col.Name)] = i
577+
}
578+
579+
var mapping []func(*sql.Context, any) (any, error)
580+
581+
for i := range fkDef.Columns {
582+
childIndex, ok := childMap[strings.ToLower(fkDef.Columns[i])]
583+
if !ok {
584+
return nil, fmt.Errorf("foreign key `%s` refers to column `%s` on table `%s` but it could not be found",
585+
fkDef.Name, fkDef.Columns[i], fkDef.Table)
586+
}
587+
parentIndex, ok := parentMap[strings.ToLower(fkDef.ParentColumns[i])]
588+
if !ok {
589+
return nil, fmt.Errorf("foreign key `%s` refers to column `%s` on referenced table `%s` but it could not be found",
590+
fkDef.Name, fkDef.ParentColumns[i], fkDef.ParentTable)
591+
}
592+
593+
childType := childSch[childIndex].Type
594+
parentType := parentSch[parentIndex].Type
595+
596+
childExtendedType, _ := childType.(types.ExtendedType)
597+
// if even one of the types is not an extended type, then we can't transform any values
598+
if childExtendedType == nil {
599+
return nil, nil
600+
}
601+
602+
if !childType.Equals(parentType) {
603+
parentExtendedType, _ := parentType.(types.ExtendedType)
604+
if mapping == nil {
605+
mapping = make([]func(*sql.Context, any) (any, error), len(childSch))
606+
}
607+
mapping[childIndex] = func(ctx *sql.Context, val any) (any, error) {
608+
return parentExtendedType.ConvertToType(ctx, childExtendedType, val)
609+
}
610+
}
611+
}
612+
613+
return mapping, nil
614+
}

sql/tables.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ type ForeignKeyTable interface {
192192
CreateIndexForForeignKey(ctx *Context, indexDef IndexDef) error
193193
// GetDeclaredForeignKeys returns the foreign key constraints that are declared by this table.
194194
GetDeclaredForeignKeys(ctx *Context) ([]ForeignKeyConstraint, error)
195-
// GetReferencedForeignKeys returns the foreign key constraints that are referenced by this table.
195+
// GetReferencedForeignKeys returns the foreign key constraints that reference this table as the parent
196196
GetReferencedForeignKeys(ctx *Context) ([]ForeignKeyConstraint, error)
197197
// AddForeignKey adds the given foreign key constraint to the table. Returns an error if the foreign key name
198198
// already exists on any other table within the database.

sql/types/extended.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ type ExtendedType interface {
3636
FormatValue(val any) (string, error)
3737
// MaxSerializedWidth returns the maximum size that the serialized value may represent.
3838
MaxSerializedWidth() ExtendedTypeSerializedWidth
39+
// ConvertToType converts the given value of the given type to this type, or returns an error if
40+
// no conversion is possible.
41+
ConvertToType(ctx *sql.Context, typ ExtendedType, val any) (any, error)
3942
}
4043

4144
type ExtendedTypeSerializedWidth uint8

0 commit comments

Comments
 (0)