From: Noah Misch <noah@leadboat.com>
To: pgsql-hackers@postgresql.org
Subject: StatisticsObjIsVisibleExt lacks "do not look in temp namespace"
Date: Sat, 20 Sep 2025 09:21:16 -0700
Message-ID: <20250920162116.2e.nmisch@google.com> (raw)
StatisticsObjIsVisibleExt() lacks the "do not look in temp namespace" code of
the rest of the non-relation, non-type namespace searches. Patch attached.
See its log messages for the consequences.
Incidentally, stats on temp tables do default to a permanent schema. That
seems fine, though:
set search_path = public;
CREATE TEMP TABLE ab1 (a INTEGER, b INTEGER, c INTEGER);
CREATE STATISTICS s ON a, b FROM ab1; -- creates public.s
\dX *.*
From: Noah Misch <noah@leadboat.com>
Fix StatisticsObjIsVisibleExt() for pg_temp.
Neighbor get_statistics_object_oid() ignores objects in pg_temp, as has
been the standard for non-relation, non-type namespace searches since
CVE-2007-2138. Hence, most operations that name a statistics object
correctly decline to map an unqualified name to a statistics object in
pg_temp. StatisticsObjIsVisibleExt() did not. Consequently,
pg_statistics_obj_is_visible() wrongly returned true for such objects,
psql \dX wrongly listed them, and getObjectDescription()-based ereport()
and pg_describe_object() wrongly omitted namespace qualification. Any
malfunction beyond that would depend on how a human or application acts
on those wrong indications. Commit
d99d58cdc8c0b5b50ee92995e8575c100b1a458a introduced this. Back-patch to
v13 (all supported versions).
Reviewed-by: FIXME
Discussion: https://postgr.es/m/FIXME
Backpatch-through: 13
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.cindex 8bd4d6c..c1e7625 100644--- a/src/backend/catalog/namespace.c+++ b/src/backend/catalog/namespace.c@@ -2685,6 +2685,9 @@ StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
{
Oid namespaceId = lfirst_oid(l);
+ if (namespaceId == myTempNamespace)+ continue; /* do not look in temp namespace */+
if (namespaceId == stxnamespace)
{
/* Found it first in path */
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.outindex a1f83b5..ff89dc4 100644--- a/src/test/regress/expected/stats_ext.out+++ b/src/test/regress/expected/stats_ext.out@@ -129,6 +129,20 @@ ALTER STATISTICS ab1_a_b_stats RENAME TO ab1_a_b_stats_new;
ERROR: must be owner of statistics object ab1_a_b_stats
RESET SESSION AUTHORIZATION;
DROP ROLE regress_stats_ext;
+CREATE STATISTICS pg_temp.stats_ext_temp ON a, b FROM ab1;+SELECT regexp_replace(pg_describe_object(tableoid, oid, 0),+ 'pg_temp_[0-9]*', 'pg_temp_REDACTED') AS descr,+ pg_statistics_obj_is_visible(oid) AS visible+ FROM pg_statistic_ext+ WHERE stxname = 'stats_ext_temp';+ descr | visible +---------------------------------------------------+---------+ statistics object pg_temp_REDACTED.stats_ext_temp | f+(1 row)++DROP STATISTICS stats_ext_temp; -- shall fail+ERROR: statistics object "stats_ext_temp" does not exist+DROP STATISTICS pg_temp.stats_ext_temp;
CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1;
NOTICE: statistics object "ab1_a_b_stats" already exists, skipping
DROP STATISTICS ab1_a_b_stats;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sqlindex 823c7db..acbb4f9 100644--- a/src/test/regress/sql/stats_ext.sql+++ b/src/test/regress/sql/stats_ext.sql@@ -83,6 +83,14 @@ DROP STATISTICS ab1_a_b_stats;
ALTER STATISTICS ab1_a_b_stats RENAME TO ab1_a_b_stats_new;
RESET SESSION AUTHORIZATION;
DROP ROLE regress_stats_ext;
+CREATE STATISTICS pg_temp.stats_ext_temp ON a, b FROM ab1;+SELECT regexp_replace(pg_describe_object(tableoid, oid, 0),+ 'pg_temp_[0-9]*', 'pg_temp_REDACTED') AS descr,+ pg_statistics_obj_is_visible(oid) AS visible+ FROM pg_statistic_ext+ WHERE stxname = 'stats_ext_temp';+DROP STATISTICS stats_ext_temp; -- shall fail+DROP STATISTICS pg_temp.stats_ext_temp;
CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1;
DROP STATISTICS ab1_a_b_stats;
Attachments:
[text/plain] stats-ext-visible-v1.patch (3.4K, ../20250920162116.2e.nmisch@google.com/2-stats-ext-visible-v1.patch)
download | inline diff:
From: Noah Misch <noah@leadboat.com>
Fix StatisticsObjIsVisibleExt() for pg_temp.
Neighbor get_statistics_object_oid() ignores objects in pg_temp, as has
been the standard for non-relation, non-type namespace searches since
CVE-2007-2138. Hence, most operations that name a statistics object
correctly decline to map an unqualified name to a statistics object in
pg_temp. StatisticsObjIsVisibleExt() did not. Consequently,
pg_statistics_obj_is_visible() wrongly returned true for such objects,
psql \dX wrongly listed them, and getObjectDescription()-based ereport()
and pg_describe_object() wrongly omitted namespace qualification. Any
malfunction beyond that would depend on how a human or application acts
on those wrong indications. Commit
d99d58cdc8c0b5b50ee92995e8575c100b1a458a introduced this. Back-patch to
v13 (all supported versions).
Reviewed-by: FIXME
Discussion: https://postgr.es/m/FIXME
Backpatch-through: 13
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.cindex 8bd4d6c..c1e7625 100644--- a/src/backend/catalog/namespace.c+++ b/src/backend/catalog/namespace.c@@ -2685,6 +2685,9 @@ StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
{
Oid namespaceId = lfirst_oid(l);
+ if (namespaceId == myTempNamespace)+ continue; /* do not look in temp namespace */+
if (namespaceId == stxnamespace)
{
/* Found it first in path */
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.outindex a1f83b5..ff89dc4 100644--- a/src/test/regress/expected/stats_ext.out+++ b/src/test/regress/expected/stats_ext.out@@ -129,6 +129,20 @@ ALTER STATISTICS ab1_a_b_stats RENAME TO ab1_a_b_stats_new;
ERROR: must be owner of statistics object ab1_a_b_stats
RESET SESSION AUTHORIZATION;
DROP ROLE regress_stats_ext;
+CREATE STATISTICS pg_temp.stats_ext_temp ON a, b FROM ab1;+SELECT regexp_replace(pg_describe_object(tableoid, oid, 0),+ 'pg_temp_[0-9]*', 'pg_temp_REDACTED') AS descr,+ pg_statistics_obj_is_visible(oid) AS visible+ FROM pg_statistic_ext+ WHERE stxname = 'stats_ext_temp';+ descr | visible +---------------------------------------------------+---------+ statistics object pg_temp_REDACTED.stats_ext_temp | f+(1 row)++DROP STATISTICS stats_ext_temp; -- shall fail+ERROR: statistics object "stats_ext_temp" does not exist+DROP STATISTICS pg_temp.stats_ext_temp;
CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1;
NOTICE: statistics object "ab1_a_b_stats" already exists, skipping
DROP STATISTICS ab1_a_b_stats;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sqlindex 823c7db..acbb4f9 100644--- a/src/test/regress/sql/stats_ext.sql+++ b/src/test/regress/sql/stats_ext.sql@@ -83,6 +83,14 @@ DROP STATISTICS ab1_a_b_stats;
ALTER STATISTICS ab1_a_b_stats RENAME TO ab1_a_b_stats_new;
RESET SESSION AUTHORIZATION;
DROP ROLE regress_stats_ext;
+CREATE STATISTICS pg_temp.stats_ext_temp ON a, b FROM ab1;+SELECT regexp_replace(pg_describe_object(tableoid, oid, 0),+ 'pg_temp_[0-9]*', 'pg_temp_REDACTED') AS descr,+ pg_statistics_obj_is_visible(oid) AS visible+ FROM pg_statistic_ext+ WHERE stxname = 'stats_ext_temp';+DROP STATISTICS stats_ext_temp; -- shall fail+DROP STATISTICS pg_temp.stats_ext_temp;
CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1;
DROP STATISTICS ab1_a_b_stats;
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: noah@leadboat.com
Subject: Re: StatisticsObjIsVisibleExt lacks "do not look in temp namespace"
In-Reply-To: <20250920162116.2e.nmisch@google.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox