diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go index a79b51fabb..62fea1b16d 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go @@ -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}}}, }, }, }, diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go index 7457767b27..c0a5f278a3 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go @@ -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 { diff --git a/go/libraries/doltcore/sqle/writer/prolly_fk_indexer.go b/go/libraries/doltcore/sqle/writer/prolly_fk_indexer.go index cc14bb9a4d..fbef8e6a5d 100644 --- a/go/libraries/doltcore/sqle/writer/prolly_fk_indexer.go +++ b/go/libraries/doltcore/sqle/writer/prolly_fk_indexer.go @@ -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.