diff --git a/pkg/sql/logictest/testdata/logic_test/temp_table b/pkg/sql/logictest/testdata/logic_test/temp_table index 88461070f602..3996cff91d4f 100644 --- a/pkg/sql/logictest/testdata/logic_test/temp_table +++ b/pkg/sql/logictest/testdata/logic_test/temp_table @@ -285,3 +285,54 @@ grant testuser to root statement ok ALTER TABLE second_db.pg_temp.a OWNER TO testuser + +# Regression test for dropping a database which contains schemas from other +# sessions. + +subtest drop_database_with_temp_schemas_from_other_sessions + +statement ok +CREATE DATABASE to_drop; +ALTER DATABASE to_drop OWNER TO testuser; + +user testuser + +statement ok +USE to_drop; +SET experimental_enable_temp_tables=true; +CREATE TABLE not_temp (i INT PRIMARY KEY); +CREATE TEMPORARY TABLE testuser_tmp (i INT PRIMARY KEY); +CREATE TEMPORARY VIEW tempuser_view AS SELECT i FROM testuser_tmp; +USE test + +user root + +statement ok +USE to_drop; +CREATE TEMPORARY TABLE root_temp (i INT PRIMARY KEY); + +let $before_drop +SELECT now() + +statement ok +USE test; +DROP DATABASE to_drop CASCADE + +query T + SELECT regexp_replace( + json_array_elements_text( + (info::JSONB)->'DroppedSchemaObjects' + ), + 'pg_temp_[^.]+.', + 'pg_temp.' + ) AS name + FROM system.eventlog + WHERE "timestamp" > '$before_drop' +ORDER BY name DESC; +---- +to_drop.public.not_temp +to_drop.pg_temp.testuser_tmp +to_drop.pg_temp.tempuser_view +to_drop.pg_temp.root_temp + +user testuser diff --git a/pkg/sql/resolver.go b/pkg/sql/resolver.go index 50ebd95f430f..5a9abaf2187f 100644 --- a/pkg/sql/resolver.go +++ b/pkg/sql/resolver.go @@ -662,22 +662,47 @@ func (p *planner) getQualifiedTableName( Required: true, IncludeOffline: true, IncludeDropped: true, + AvoidCached: true, }) if err != nil { return nil, err } + // Get the schema name. Use some specialized logic to deal with descriptors + // from other temporary schemas. + // + // TODO(ajwerner): We shouldn't need this temporary logic if we properly + // tracked all descriptors as we read them and made them available in the + // collection. We should only be hitting this edge case when dropping a + // database, in which case we've already read all of the temporary schema + // information from the namespace table. + var schemaName tree.Name schemaID := desc.GetParentSchemaID() scDesc, err := p.Descriptors().GetImmutableSchemaByID(ctx, p.txn, schemaID, tree.SchemaLookupFlags{ IncludeOffline: true, IncludeDropped: true, + AvoidCached: true, }) - if err != nil { - return nil, err + switch { + case err == nil: + schemaName = tree.Name(scDesc.GetName()) + case desc.IsTemporary() && errors.Is(err, catalog.ErrDescriptorNotFound): + // We've lost track of the session which owned this schema, but we + // can come up with a name that is also going to be unique and + // informative and looks like a pg_temp_ name. + schemaName = tree.Name(fmt.Sprintf("pg_temp_%d", schemaID)) + default: + return nil, errors.Wrapf(err, + "resolving schema name for %s.[%d].%s", + tree.Name(dbDesc.GetName()), + schemaID, + tree.Name(desc.GetName()), + ) } + tbName := tree.MakeTableNameWithSchema( tree.Name(dbDesc.GetName()), - tree.Name(scDesc.GetName()), + schemaName, tree.Name(desc.GetName()), ) return &tbName, nil