Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foreign Keys: INSERT planning #13676

Merged
merged 12 commits into from
Aug 8, 2023
Prev Previous commit
Next Next commit
feat: add unit tests for foreign_keys_planning
Signed-off-by: Manan Gupta <manan@planetscale.com>
GuptaManan100 authored and harshit-gangal committed Aug 7, 2023
commit 914117f45a24a98254feacec134b1d05326221bb
9 changes: 8 additions & 1 deletion go/vt/vtgate/planbuilder/operators/fk_verify.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops"
"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
"vitess.io/vitess/go/vt/vtgate/semantics"
"vitess.io/vitess/go/vt/vtgate/vindexes"
)

@@ -71,7 +72,13 @@ func NewFkVerify(ctx *plancontext.PlanningContext, parentFKs []vindexes.ParentFK
),
),
}
op, err := PlanQuery(ctx, query)

newSemTable, err := semantics.Analyze(query, fk.Table.Keyspace.Name, ctx.VSchema)
if err != nil {
return nil, err
}
newCtx := plancontext.NewPlanningContext(nil, newSemTable, ctx.VSchema, ctx.PlannerVersion)
op, err := PlanQuery(newCtx, query)
if err != nil {
return nil, err
}
43 changes: 42 additions & 1 deletion go/vt/vtgate/planbuilder/plan_test.go
Original file line number Diff line number Diff line change
@@ -100,6 +100,21 @@ func TestPlan(t *testing.T) {
testFile(t, "misc_cases.json", testOutputTempDir, vschemaWrapper, false)
}

// TestForeignKeyPlanning tests the planning of foreign keys in a managed mode by Vitess.
func TestForeignKeyPlanning(t *testing.T) {
vschemaWrapper := &vschemaWrapper{
v: loadSchema(t, "vschemas/schema.json", true),
// Set the keyspace with foreign keys enabled as the default.
keyspace: &vindexes.Keyspace{
Name: "user_fk_allow",
Sharded: true,
},
}
testOutputTempDir := makeTestOutput(t)

testFile(t, "fk_cases.json", testOutputTempDir, vschemaWrapper, false)
}

func TestSystemTables57(t *testing.T) {
// first we move everything to use 5.7 logic
servenv.SetMySQLServerVersionForTest("5.7")
@@ -418,9 +433,31 @@ func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSch
}
}
}
if vschema.Keyspaces["user_fk_allow"] != nil {
// FK from multicol_tbl2 referencing multicol_tbl1 that is shard scoped.
err = vschema.AddForeignKey("user_fk_allow", "multicol_tbl2", createFkDefinition([]string{"colb", "cola", "x", "colc", "y"}, "multicol_tbl1", []string{"colb", "cola", "y", "colc", "x"}))
require.NoError(t, err)
// FK from tbl2 referencing tbl1 that is shard scoped.
err = vschema.AddForeignKey("user_fk_allow", "tbl2", createFkDefinition([]string{"col2"}, "tbl1", []string{"col1"}))
require.NoError(t, err)
// FK from tbl3 referencing tbl1 that is not shard scoped.
err = vschema.AddForeignKey("user_fk_allow", "tbl3", createFkDefinition([]string{"coly"}, "tbl1", []string{"col1"}))
require.NoError(t, err)
}
return vschema
}

// createFkDefinition is a helper function to create a Foreign key definition struct from the columns used in it provided as list of strings.
func createFkDefinition(childCols []string, parentTableName string, parentCols []string) *sqlparser.ForeignKeyDefinition {
return &sqlparser.ForeignKeyDefinition{
Source: sqlparser.MakeColumns(childCols...),
ReferenceDefinition: &sqlparser.ReferenceDefinition{
ReferencedTable: sqlparser.NewTableName(parentTableName),
ReferencedColumns: sqlparser.MakeColumns(parentCols...),
},
}
}

var _ plancontext.VSchema = (*vschemaWrapper)(nil)

type vschemaWrapper struct {
@@ -508,7 +545,11 @@ func (vw *vschemaWrapper) PlannerWarning(_ string) {
}

func (vw *vschemaWrapper) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) {
return vschemapb.Keyspace_FK_UNMANAGED, nil
defaultFkMode := vschemapb.Keyspace_FK_UNMANAGED
if vw.v.Keyspaces[keyspace] != nil && vw.v.Keyspaces[keyspace].ForeignKeyMode != vschemapb.Keyspace_FK_DEFAULT {
return vw.v.Keyspaces[keyspace].ForeignKeyMode, nil
}
return defaultFkMode, nil
}

func (vw *vschemaWrapper) AllKeyspace() ([]*vindexes.Keyspace, error) {
57 changes: 57 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/fk_cases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[
{
"comment": "Insertion in a table with cross-shard foreign keys disallowed",
"query": "insert into tbl3 (col3, coly) values (1, 3)",
"plan": "VT12002: unsupported cross-shard foreign keys"
},
{
"comment": "Insertion in a table with shard-scoped foreign keys is allowed",
"query": "insert into tbl2 (col2, coly) values (1, 3)",
"plan": {
"QueryType": "INSERT",
"Original": "insert into tbl2 (col2, coly) values (1, 3)",
"Instructions": {
"OperatorType": "Insert",
"Variant": "Sharded",
"Keyspace": {
"Name": "user_fk_allow",
"Sharded": true
},
"TargetTabletType": "PRIMARY",
"Query": "insert into tbl2(col2, coly) values (:_col2_0, 3)",
"TableName": "tbl2",
"VindexValues": {
"hash_vin": "INT64(1)"
}
},
"TablesUsed": [
"user_fk_allow.tbl2"
]
}
},
{
"comment": "Insertion in a table with shard-scoped multiple column foreign key is allowed",
"query": "insert into multicol_tbl2 (cola, colb, colc) values (1, 2, 3)",
"plan": {
"QueryType": "INSERT",
"Original": "insert into multicol_tbl2 (cola, colb, colc) values (1, 2, 3)",
"Instructions": {
"OperatorType": "Insert",
"Variant": "Sharded",
"Keyspace": {
"Name": "user_fk_allow",
"Sharded": true
},
"TargetTabletType": "PRIMARY",
"Query": "insert into multicol_tbl2(cola, colb, colc) values (:_cola_0, :_colb_0, :_colc_0)",
"TableName": "multicol_tbl2",
"VindexValues": {
"multicolIdx": "INT64(1), INT64(2), INT64(3)"
}
},
"TablesUsed": [
"user_fk_allow.multicol_tbl2"
]
}
}
]
63 changes: 63 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/vschemas/schema.json
Original file line number Diff line number Diff line change
@@ -602,6 +602,69 @@
]
}
}
},
"user_fk_allow": {
"sharded": true,
"foreignKeyMode": "FK_MANAGED",
"vindexes": {
"hash_vin": {
"type": "hash_test",
"owner": "user"
},
"multicolIdx": {
"type": "multiCol_test"
}
},
"tables": {
"multicol_tbl1": {
"column_vindexes": [
{
"columns": [
"cola",
"colb",
"colc"
],
"name": "multicolIdx"
}
]
},
"multicol_tbl2": {
"column_vindexes": [
{
"columns": [
"cola",
"colb",
"colc"
],
"name": "multicolIdx"
}
]
},
"tbl1": {
"column_vindexes": [
{
"column": "col1",
"name": "hash_vin"
}
]
},
"tbl2": {
"column_vindexes": [
{
"column": "col2",
"name": "hash_vin"
}
]
},
"tbl3": {
"column_vindexes": [
{
"column": "col3",
"name": "hash_vin"
}
]
}
}
}
}
}
4 changes: 2 additions & 2 deletions go/vt/vtgate/vindexes/foreign_keys.go
Original file line number Diff line number Diff line change
@@ -71,8 +71,8 @@ func NewChildFkInfo(childTbl *Table, fkDef *sqlparser.ForeignKeyDefinition) Chil
}
}

// addForeignKey is for testing only.
func (vschema *VSchema) addForeignKey(ksname, childTableName string, fkConstraint *sqlparser.ForeignKeyDefinition) error {
// AddForeignKey is for testing only.
func (vschema *VSchema) AddForeignKey(ksname, childTableName string, fkConstraint *sqlparser.ForeignKeyDefinition) error {
ks, ok := vschema.Keyspaces[ksname]
if !ok {
return fmt.Errorf("keyspace %s not found in vschema", ksname)
2 changes: 1 addition & 1 deletion go/vt/vtgate/vindexes/vschema_test.go
Original file line number Diff line number Diff line change
@@ -411,7 +411,7 @@ func TestVSchemaForeignKeys(t *testing.T) {
require.NoError(t, vschema.Keyspaces["main"].Error)

// add fk containst a keyspace.
vschema.addForeignKey("main", "t1", &sqlparser.ForeignKeyDefinition{
vschema.AddForeignKey("main", "t1", &sqlparser.ForeignKeyDefinition{
Source: sqlparser.Columns{sqlparser.NewIdentifierCI("c2")},
ReferenceDefinition: &sqlparser.ReferenceDefinition{
ReferencedTable: sqlparser.NewTableName("t1"),