mirror of
https://github.com/dolthub/dolt.git
synced 2026-03-16 23:53:17 -05:00
Merge pull request #4239 from dolthub/andy/fk-panic
[no-release-notes] go/doltcore/sqle/writer: Fix `prollyFkIndexer` for primary keys not at the start of a schema
This commit is contained in:
@@ -109,85 +109,47 @@ func TestSingleQuery(t *testing.T) {
|
||||
// Convenience test for debugging a single query. Unskip and set to the desired query.
|
||||
func TestSingleScript(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
var scripts = []queries.ScriptTest{
|
||||
{
|
||||
Name: "truncate table",
|
||||
Name: "Nautobot FOREIGN KEY panic repro",
|
||||
SetUpScript: []string{
|
||||
"create table t (a int primary key auto_increment, b int)",
|
||||
"call dolt_add('.')",
|
||||
"call dolt_commit('-am', 'empty table')",
|
||||
"call dolt_branch('branch1')",
|
||||
"call dolt_branch('branch2')",
|
||||
"insert into t (b) values (1), (2)",
|
||||
"call dolt_commit('-am', 'two values on main')",
|
||||
"call dolt_checkout('branch1')",
|
||||
"insert into t (b) values (3), (4)",
|
||||
"call dolt_commit('-am', 'two values on branch1')",
|
||||
"call dolt_checkout('branch2')",
|
||||
"insert into t (b) values (5), (6)",
|
||||
"call dolt_checkout('branch1')",
|
||||
"CREATE TABLE `auth_user` (" +
|
||||
" `password` varchar(128) NOT NULL," +
|
||||
" `last_login` datetime," +
|
||||
" `is_superuser` tinyint NOT NULL," +
|
||||
" `username` varchar(150) NOT NULL," +
|
||||
" `first_name` varchar(150) NOT NULL," +
|
||||
" `last_name` varchar(150) NOT NULL," +
|
||||
" `email` varchar(254) NOT NULL," +
|
||||
" `is_staff` tinyint NOT NULL," +
|
||||
" `is_active` tinyint NOT NULL," +
|
||||
" `date_joined` datetime NOT NULL," +
|
||||
" `id` char(32) NOT NULL," +
|
||||
" `config_data` json NOT NULL," +
|
||||
" PRIMARY KEY (`id`)," +
|
||||
" UNIQUE KEY `username` (`username`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin",
|
||||
"CREATE TABLE `users_token` (" +
|
||||
" `id` char(32) NOT NULL," +
|
||||
" `created` datetime NOT NULL," +
|
||||
" `expires` datetime," +
|
||||
" `key` varchar(40) NOT NULL," +
|
||||
" `write_enabled` tinyint NOT NULL," +
|
||||
" `description` varchar(200) NOT NULL," +
|
||||
" `user_id` char(32) NOT NULL," +
|
||||
" PRIMARY KEY (`id`)," +
|
||||
" UNIQUE KEY `key` (`key`)," +
|
||||
" KEY `users_token_user_id_af964690` (`user_id`)," +
|
||||
" CONSTRAINT `users_token_user_id_af964690_fk_auth_user_id` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"INSERT INTO `auth_user` (`password`,`last_login`,`is_superuser`,`username`,`first_name`,`last_name`,`email`,`is_staff`,`is_active`,`date_joined`,`id`,`config_data`)" +
|
||||
"VALUES ('pbkdf2_sha256$216000$KRpZeDPgwc5E$vl/2hwrmtnckaBT0A8pf63Ph+oYuCHYI7qozMTZihTo=',NULL,1,'admin','','','admin@example.com',1,1,'2022-08-30 18:27:21.810049','1056443cc03446c592fa4c06bb06a1a6','{}');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "truncate table t",
|
||||
Expected: []sql.Row{{sql.NewOkResult(2)}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_checkout('main')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
// highest value in any branch is 6
|
||||
Query: "insert into t (b) values (7), (8)",
|
||||
Expected: []sql.Row{{sql.OkResult{RowsAffected: 2, InsertID: 7}}},
|
||||
},
|
||||
{
|
||||
Query: "select * from t order by a",
|
||||
Expected: []sql.Row{
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{7, 7},
|
||||
{8, 8},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "truncate table t",
|
||||
Expected: []sql.Row{{sql.NewOkResult(4)}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_checkout('branch2')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
// highest value in any branch is still 6 (truncated table above)
|
||||
Query: "insert into t (b) values (7), (8)",
|
||||
Expected: []sql.Row{{sql.OkResult{RowsAffected: 2, InsertID: 7}}},
|
||||
},
|
||||
{
|
||||
Query: "select * from t order by a",
|
||||
Expected: []sql.Row{
|
||||
{5, 5},
|
||||
{6, 6},
|
||||
{7, 7},
|
||||
{8, 8},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "truncate table t",
|
||||
Expected: []sql.Row{{sql.NewOkResult(4)}},
|
||||
},
|
||||
{
|
||||
// no value on any branch
|
||||
Query: "insert into t (b) values (1), (2)",
|
||||
Expected: []sql.Row{{sql.OkResult{RowsAffected: 2, InsertID: 1}}},
|
||||
},
|
||||
{
|
||||
Query: "select * from t order by a",
|
||||
Expected: []sql.Row{
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
},
|
||||
Query: "INSERT INTO `users_token` (`id`, `user_id`, `created`, `expires`, `key`, `write_enabled`, `description`) " +
|
||||
"VALUES ('acc2e157db2845a79221cc654b1dcecc', '1056443cc03446c592fa4c06bb06a1a6', '2022-08-30 18:27:21.948487', NULL, '0123456789abcdef0123456789abcdef01234567', 1, '');",
|
||||
Expected: []sql.Row{{sql.OkResult{RowsAffected: 0x1, InsertID: 0x0}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -658,6 +658,49 @@ var DoltScripts = []queries.ScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Nautobot FOREIGN KEY panic repro",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE `auth_user` (" +
|
||||
" `password` varchar(128) NOT NULL," +
|
||||
" `last_login` datetime," +
|
||||
" `is_superuser` tinyint NOT NULL," +
|
||||
" `username` varchar(150) NOT NULL," +
|
||||
" `first_name` varchar(150) NOT NULL," +
|
||||
" `last_name` varchar(150) NOT NULL," +
|
||||
" `email` varchar(254) NOT NULL," +
|
||||
" `is_staff` tinyint NOT NULL," +
|
||||
" `is_active` tinyint NOT NULL," +
|
||||
" `date_joined` datetime NOT NULL," +
|
||||
" `id` char(32) NOT NULL," +
|
||||
" `config_data` json NOT NULL," +
|
||||
" PRIMARY KEY (`id`)," +
|
||||
" UNIQUE KEY `username` (`username`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin",
|
||||
"CREATE TABLE `users_token` (" +
|
||||
" `id` char(32) NOT NULL," +
|
||||
" `created` datetime NOT NULL," +
|
||||
" `expires` datetime," +
|
||||
" `key` varchar(40) NOT NULL," +
|
||||
" `write_enabled` tinyint NOT NULL," +
|
||||
" `description` varchar(200) NOT NULL," +
|
||||
" `user_id` char(32) NOT NULL," +
|
||||
" PRIMARY KEY (`id`)," +
|
||||
" UNIQUE KEY `key` (`key`)," +
|
||||
" KEY `users_token_user_id_af964690` (`user_id`)," +
|
||||
" CONSTRAINT `users_token_user_id_af964690_fk_auth_user_id` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"INSERT INTO `auth_user` (`password`,`last_login`,`is_superuser`,`username`,`first_name`,`last_name`,`email`,`is_staff`,`is_active`,`date_joined`,`id`,`config_data`)" +
|
||||
"VALUES ('pbkdf2_sha256$216000$KRpZeDPgwc5E$vl/2hwrmtnckaBT0A8pf63Ph+oYuCHYI7qozMTZihTo=',NULL,1,'admin','','','admin@example.com',1,1,'2022-08-30 18:27:21.810049','1056443cc03446c592fa4c06bb06a1a6','{}');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO `users_token` (`id`, `user_id`, `created`, `expires`, `key`, `write_enabled`, `description`) " +
|
||||
"VALUES ('acc2e157db2845a79221cc654b1dcecc', '1056443cc03446c592fa4c06bb06a1a6', '2022-08-30 18:27:21.948487', NULL, '0123456789abcdef0123456789abcdef01234567', 1, '');",
|
||||
Expected: []sql.Row{{sql.OkResult{RowsAffected: 0x1, InsertID: 0x0}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func makeLargeInsert(sz int) string {
|
||||
|
||||
@@ -79,7 +79,7 @@ func (n *prollyFkIndexer) PartitionRows(ctx *sql.Context, _ sql.Partition) (sql.
|
||||
}
|
||||
|
||||
pkToIdxMap := make(val.OrdinalMapping, n.writer.sch.GetPKCols().Size())
|
||||
for j, idxCol := range n.index.IndexSchema().GetAllCols().GetColumns() {
|
||||
for j, idxCol := range n.index.IndexSchema().GetPKCols().GetColumns() {
|
||||
if i, ok := n.writer.sch.GetPKCols().TagToIdx[idxCol.Tag]; ok {
|
||||
pkToIdxMap[i] = j
|
||||
}
|
||||
@@ -121,41 +121,49 @@ var _ sql.RowIter = prollyFkPkRowIter{}
|
||||
|
||||
// Next implements the interface sql.RowIter.
|
||||
func (iter prollyFkPkRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
// |rangeIter| iterates on the foreign key index of the parent table
|
||||
k, _, err := iter.rangeIter.Next(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k == nil {
|
||||
return nil, io.EOF
|
||||
}
|
||||
for {
|
||||
// |rangeIter| iterates on the foreign key index of the parent table
|
||||
k, _, err := iter.rangeIter.Next(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k == nil {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
pkBld := iter.primary.keyBld
|
||||
for pkPos, idxPos := range iter.pkToIdxMap {
|
||||
pkBld.PutRaw(pkPos, k.GetField(idxPos))
|
||||
}
|
||||
pkTup := pkBld.BuildPermissive(sharePool)
|
||||
pkBld := iter.primary.keyBld
|
||||
for pkPos, idxPos := range iter.pkToIdxMap {
|
||||
pkBld.PutRaw(pkPos, k.GetField(idxPos))
|
||||
}
|
||||
pkTup := pkBld.BuildPermissive(sharePool)
|
||||
|
||||
nextRow := make(sql.Row, len(iter.primary.keyMap)+len(iter.primary.valMap))
|
||||
err = iter.primary.mut.Get(ctx, pkTup, func(tblKey, tblVal val.Tuple) error {
|
||||
var tblKey, tblVal val.Tuple
|
||||
err = iter.primary.mut.Get(ctx, pkTup, func(k, v val.Tuple) error {
|
||||
tblKey, tblVal = k, v
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tblKey == nil {
|
||||
continue // referential integrity broken
|
||||
}
|
||||
|
||||
nextRow := make(sql.Row, len(iter.primary.keyMap)+len(iter.primary.valMap))
|
||||
for from := range iter.primary.keyMap {
|
||||
to := iter.primary.keyMap.MapOrdinal(from)
|
||||
if nextRow[to], err = index.GetField(ctx, iter.primary.keyBld.Desc, from, tblKey, iter.primary.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for from := range iter.primary.valMap {
|
||||
to := iter.primary.valMap.MapOrdinal(from)
|
||||
if nextRow[to], err = index.GetField(ctx, iter.primary.valBld.Desc, from, tblVal, iter.primary.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nextRow, nil
|
||||
}
|
||||
return nextRow, nil
|
||||
}
|
||||
|
||||
// Close implements the interface sql.RowIter.
|
||||
|
||||
Reference in New Issue
Block a user