diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 27fd7e9720a..a5b4bc5b7d5 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -2586,9 +2586,23 @@ ExtendBufferedRelCommon(BufferManagerRelation bmr, extend_by); if (bmr.relpersistence == RELPERSISTENCE_TEMP) + { + /* + * Reject attempts to extend non-local temporary relations; we have no + * ability to transfer about-to-be-created local buffers into the + * owning session's local buffers. This is the canonical place for + * the check, covering any attempt to extend a non-local temporary + * relation. + */ + if (bmr.rel && RELATION_IS_OTHER_TEMP(bmr.rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + first_block = ExtendBufferedRelLocal(bmr, fork, flags, extend_by, extend_upto, buffers, &extend_by); + } else first_block = ExtendBufferedRelShared(bmr, fork, strategy, flags, extend_by, extend_upto, diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 2f86c79a907..09c794239ca 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -668,10 +668,10 @@ RelationCloseSmgr(Relation relation) * the owning session keeps the data in its private local buffer pool, * which we cannot access. Existing buffer-manager entry points * (ReadBuffer_common(), StartReadBuffersImpl(), read_stream_begin_impl(), - * and PrefetchBuffer()) already enforce this; any new buffer-access entry - * points must do the same. Command-level code (TRUNCATE, ALTER TABLE, - * VACUUM, CLUSTER, REINDEX, ...) additionally uses this macro for - * command-specific error messages. + * PrefetchBuffer() and ExtendBufferedRelCommon()) already enforce this; any + * new buffer-access entry points must do the same. Command-level code + * (TRUNCATE, ALTER TABLE, VACUUM, CLUSTER, REINDEX, ...) additionally uses + * this macro for command-specific error messages. * * Beware of multiple eval of argument */ diff --git a/src/test/modules/test_misc/t/013_temp_obj_multisession.pl b/src/test/modules/test_misc/t/013_temp_obj_multisession.pl index c56a032e57f..0e28ed59d05 100644 --- a/src/test/modules/test_misc/t/013_temp_obj_multisession.pl +++ b/src/test/modules/test_misc/t/013_temp_obj_multisession.pl @@ -37,6 +37,10 @@ my $psql1 = $node->background_psql('postgres'); # masked by an index scan that would hit ReadBuffer_common from nbtree. $psql1->query_safe(q(CREATE TEMP TABLE foo AS SELECT 42 AS val;)); +# Also create an empty table, so read path go straight through the +# extend-relation entry point. +$psql1->query_safe(q(CREATE TEMP TABLE empty_foo (val INT);)); + # Resolve the owner's temp schema so the probing session can refer to # the table by a fully-qualified name. my $tempschema = $node->safe_psql( @@ -67,6 +71,18 @@ like( qr/cannot access temporary tables of other sessions/, 'SELECT (seqscan via read_stream)'); +# INSERT into empty table goes through hio.c which calls RelationAddBlocks() to +# extend the table; that hits the check before new pages are created for the +# table. +$node->psql( + 'postgres', + "INSERT INTO $tempschema.empty_foo VALUES (42);", + stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary tables of other sessions/, + 'INSERT (caught via hio.c)'); + # INSERT goes through hio.c which calls ReadBufferExtended() to find a # page with free space; that hits the existing check before any data # is written.