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

release-20.1: combined fixes for ownership related bugs #51629

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/sql/alter_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ func (n *alterTableNode) startExec(params runParams) error {
return err
}

if err := params.p.dropSequencesOwnedByCol(params.ctx, colToDrop); err != nil {
if err := params.p.dropSequencesOwnedByCol(params.ctx, colToDrop, true /* queueJob */); err != nil {
return err
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/drop_sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ func (p *planner) dropSequenceImpl(
jobDesc string,
behavior tree.DropBehavior,
) error {
if err := removeSequenceOwnerIfExists(ctx, p, seqDesc.ID, seqDesc.GetSequenceOpts()); err != nil {
return err
}
return p.initiateDropTable(ctx, seqDesc, queueJob, jobDesc, true /* drainName */)
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/drop_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ func (p *planner) dropTableImpl(

// Drop sequences that the columns of the table own
for _, col := range tableDesc.Columns {
if err := p.dropSequencesOwnedByCol(ctx, &col); err != nil {
if err := p.dropSequencesOwnedByCol(ctx, &col, queueJob); err != nil {
return droppedViews, err
}
}
Expand Down Expand Up @@ -336,7 +336,7 @@ func (p *planner) initiateDropTable(
drainName bool,
) error {
if tableDesc.Dropped() {
return fmt.Errorf("table %q is being dropped", tableDesc.Name)
return errors.Errorf("table %q is already being dropped", tableDesc.Name)
}

// If the table is not interleaved , use the delayed GC mechanism to
Expand Down
146 changes: 146 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/sequences
Original file line number Diff line number Diff line change
Expand Up @@ -1071,3 +1071,149 @@ DROP TABLE b;

statement ok
DROP TABLE a;
subtest regression_50649

statement ok
CREATE TABLE t_50649(a INT PRIMARY KEY)

statement ok
CREATE SEQUENCE seq_50649 OWNED BY t_50649.a

statement ok
DROP SEQUENCE seq_50649

statement ok
DROP TABLE t_50649

subtest regression_50712
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of confusing how tests from a different commit ended up in this one, but I don't know if it's worth the trouble to fix.


statement ok
CREATE DATABASE db_50712

statement ok
CREATE TABLE db_50712.t_50712(a INT PRIMARY KEY)

statement ok
CREATE SEQUENCE db_50712.seq_50712 OWNED BY db_50712.t_50712.a

statement ok
DROP DATABASE db_50712 CASCADE

# Same test like above, except the table is lexicographically less than the
# sequence, which results in drop database dropping the table before the
# sequence.
statement ok
CREATE DATABASE db_50712

statement ok
CREATE TABLE db_50712.a_50712(a INT PRIMARY KEY)

statement ok
CREATE SEQUENCE db_50712.seq_50712 OWNED BY db_50712.a_50712.a

statement ok
DROP DATABASE db_50712 CASCADE

# Same test like above, except the db is switched as the current db
statement ok
CREATE DATABASE db_50712

statement ok
SET DATABASE = db_50712

statement ok
CREATE TABLE a_50712(a INT PRIMARY KEY)

statement ok
CREATE SEQUENCE seq_50712 OWNED BY a_50712.a

statement ok
DROP DATABASE db_50712

statement ok
SET DATABASE = test

# Tests db drop.
# Sequence: outside db.
# Owner: inside db.
# The sequence should be automatically dropped.
statement ok
CREATE DATABASE db_50712

statement ok
CREATE TABLE db_50712.t_50712(a INT PRIMARY KEY)

statement ok
CREATE SEQUENCE seq_50712 OWNED BY db_50712.t_50712.a

statement ok
DROP DATABASE db_50712 CASCADE

statement error pq: relation "seq_50712" does not exist
SELECT * FROM seq_50712

# Tests db drop.
# Sequence: inside db
# Owner: outside db
# It should be possible to drop the table later.
statement ok
CREATE DATABASE db_50712

statement ok
CREATE TABLE t_50712(a INT PRIMARY KEY)

statement ok
CREATE SEQUENCE db_50712.seq_50712 OWNED BY t_50712.a

statement ok
DROP DATABASE db_50712 CASCADE

statement ok
DROP TABLE t_50712
<<<<<<< HEAD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Ideally you'd remove this line in this commit rather than a later commit in the PR.


# previously, changing ownership of a sequence between columns of the same table
# was causing the sequenceID to appear in multiple column's ownedSequences list.
# This makes it impossible to drop the affected columns/table once the sequence
# has been dropped. This tests for these scenarios and ensures the table/columns
# remain drop-able.
subtest regression_50711

statement ok
CREATE TABLE t_50711(a int, b int)

statement ok
CREATE SEQUENCE seq_50711 owned by t_50711.a

statement ok
ALTER SEQUENCE seq_50711 owned by t_50711.b

statement ok
ALTER SEQUENCE seq_50711 owned by t_50711.a

statement ok
DROP SEQUENCE seq_50711

statement ok
DROP TABLE t_50711

statement ok
CREATE TABLE t_50711(a int, b int)

statement ok
CREATE SEQUENCE seq_50711 owned by t_50711.a

statement ok
ALTER SEQUENCE seq_50711 owned by t_50711.b

statement ok
ALTER SEQUENCE seq_50711 owned by t_50711.a

statement ok
DROP SEQUENCE seq_50711

statement ok
ALTER TABLE t_50711 DROP COLUMN a

statement ok
ALTER TABLE t_50711 DROP COLUMN b
23 changes: 18 additions & 5 deletions pkg/sql/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func assignSequenceOptions(
if err := removeSequenceOwnerIfExists(params.ctx, params.p, sequenceID, opts); err != nil {
return err
}
err := addSequenceOwner(params.ctx, params.p, tableDesc, col, sequenceID, opts)
err := addSequenceOwner(params.ctx, params.p, option.ColumnItemVal, sequenceID, opts)
if err != nil {
return err
}
Expand Down Expand Up @@ -336,6 +336,11 @@ func removeSequenceOwnerIfExists(
if err != nil {
return err
}
// If the table descriptor has already been dropped, there is no need to
// remove the reference.
if tableDesc.Dropped() {
return nil
}
col, err := tableDesc.FindColumnByID(opts.SequenceOwner.OwnerColumnID)
if err != nil {
return err
Expand Down Expand Up @@ -384,11 +389,15 @@ func resolveColumnItemToDescriptors(
func addSequenceOwner(
ctx context.Context,
p *planner,
tableDesc *MutableTableDescriptor,
col *sqlbase.ColumnDescriptor,
columnItemVal *tree.ColumnItem,
sequenceID sqlbase.ID,
opts *sqlbase.TableDescriptor_SequenceOpts,
) error {
tableDesc, col, err := resolveColumnItemToDescriptors(ctx, p, columnItemVal)
if err != nil {
return err
}

col.OwnsSequenceIds = append(col.OwnsSequenceIds, sequenceID)

opts.SequenceOwner.OwnerColumnID = col.ID
Expand Down Expand Up @@ -467,17 +476,21 @@ func maybeAddSequenceDependencies(
// dropSequencesOwnedByCol drops all the sequences from col.OwnsSequenceIDs.
// Called when the respective column (or the whole table) is being dropped.
func (p *planner) dropSequencesOwnedByCol(
ctx context.Context, col *sqlbase.ColumnDescriptor,
ctx context.Context, col *sqlbase.ColumnDescriptor, queueJob bool,
) error {
for _, sequenceID := range col.OwnsSequenceIds {
seqDesc, err := p.Tables().getMutableTableVersionByID(ctx, sequenceID, p.txn)
if err != nil {
return err
}
// This sequence is already getting dropped. Don't do it twice.
if seqDesc.Dropped() {
continue
}
jobDesc := fmt.Sprintf("removing sequence %q dependent on column %q which is being dropped",
seqDesc.Name, col.ColName())
if err := p.dropSequenceImpl(
ctx, seqDesc, true /* queueJob */, jobDesc, tree.DropRestrict,
ctx, seqDesc, queueJob, jobDesc, tree.DropRestrict,
); err != nil {
return err
}
Expand Down
119 changes: 119 additions & 0 deletions pkg/sql/sequence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import (
"testing"

"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/kv"
"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
)

func BenchmarkSequenceIncrement(b *testing.B) {
Expand Down Expand Up @@ -70,3 +74,118 @@ func BenchmarkUniqueRowID(b *testing.B) {
}
b.StopTimer()
}

// Regression test for #50711. The root cause of #50711 was the fact that a
// sequenceID popped up in multiple columns' column descriptor. This test
// inspects the table descriptor to ascertain that sequence ownership integrity
// is preserved in various scenarios.
// Scenarios tested:
// - ownership swaps between table columns
// - two sequences being owned simultaneously
// - sequence drops
// - ownership removal
func TestSequenceOwnershipDependencies(t *testing.T) {
defer leaktest.AfterTest(t)()

ctx := context.Background()
params := base.TestServerArgs{}
s, sqlConn, kvDB := serverutils.StartServer(t, params)
defer s.Stopper().Stop(ctx)

if _, err := sqlConn.Exec(`
CREATE DATABASE t;
CREATE TABLE t.test(a INT PRIMARY KEY, b INT)`); err != nil {
t.Fatal(err)
}

// Switch ownership between columns of the same table.
if _, err := sqlConn.Exec("CREATE SEQUENCE t.seq1 OWNED BY t.test.a"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, []string{"seq1"})
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, nil /* seqNames */)

if _, err := sqlConn.Exec("ALTER SEQUENCE t.seq1 OWNED BY t.test.b"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, nil /* seqNames */)
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, []string{"seq1"})

if _, err := sqlConn.Exec("ALTER SEQUENCE t.seq1 OWNED BY t.test.a"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, []string{"seq1"})
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, nil /* seqNames */)

// Add a second sequence in the mix and switch its ownership.
if _, err := sqlConn.Exec("CREATE SEQUENCE t.seq2 OWNED BY t.test.a"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, []string{"seq1", "seq2"})
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, nil /* seqNames */)

if _, err := sqlConn.Exec("ALTER SEQUENCE t.seq2 OWNED BY t.test.b"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, []string{"seq1"})
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, []string{"seq2"})

if _, err := sqlConn.Exec("ALTER SEQUENCE t.seq2 OWNED BY t.test.a"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, []string{"seq1", "seq2"})
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, nil /* seqNames */)

// Ensure dropping sequences removes the ownership dependencies.
if _, err := sqlConn.Exec("DROP SEQUENCE t.seq1"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, []string{"seq2"})
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, nil /* seqNames */)

// Ensure removing an owner removes the ownership dependency.
if _, err := sqlConn.Exec("ALTER SEQUENCE t.seq2 OWNED BY NONE"); err != nil {
t.Fatal(err)
}
assertColumnOwnsSequences(t, kvDB, "t", "test", 0 /* colIdx */, nil /* seqNames */)
assertColumnOwnsSequences(t, kvDB, "t", "test", 1 /* colIdx */, nil /* seqNames */)
}

// assertColumnOwnsSequences ensures that the column at (DbName, tbName, colIdx)
// owns all the sequences passed to it (in order) by looking up descriptors in
// kvDB.
func assertColumnOwnsSequences(
t *testing.T, kvDB *kv.DB, dbName string, tbName string, colIdx int, seqNames []string,
) {
tableDesc := sqlbase.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, dbName, tbName)
col := tableDesc.GetColumns()[colIdx]
var seqDescs []*SequenceDescriptor
for _, seqName := range seqNames {
seqDescs = append(
seqDescs,
sqlbase.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, dbName, seqName),
)
}

if len(col.OwnsSequenceIds) != len(seqDescs) {
t.Fatalf(
"unexpected number of sequence ownership dependencies. expected: %d, got:%d",
len(seqDescs), len(col.OwnsSequenceIds),
)
}

for i, seqID := range col.OwnsSequenceIds {
if seqID != seqDescs[i].GetID() {
t.Fatalf("unexpected sequence id. expected %d got %d", seqDescs[i].GetID(), seqID)
}

ownerTableID := seqDescs[i].SequenceOpts.SequenceOwner.OwnerTableID
ownerColID := seqDescs[i].SequenceOpts.SequenceOwner.OwnerColumnID
if ownerTableID != tableDesc.GetID() || ownerColID != col.ID {
t.Fatalf(
"unexpected sequence owner. expected table id %d, got: %d; expected column id %d, got :%d",
tableDesc.GetID(), ownerTableID, col.ID, ownerColID,
)
}
}
}
Loading