[SCM] PostGIS branch master updated. 3.6.0rc2-624-g6f11ffd05
git at osgeo.org
git at osgeo.org
Fri Jun 19 12:45:05 PDT 2026
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".
The branch, master has been updated
via 6f11ffd051915508ef244399043b484a2de46295 (commit)
from 767201c79e8fdd691b8f244df0b6c74f6118c901 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 6f11ffd051915508ef244399043b484a2de46295
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Fri Jun 19 23:42:36 2026 +0400
postgis: avoid stale geometry_columns relation lookups
Filter stale pg_class rows from geometry_columns by re-resolving visible schema-qualified names before exposing metadata, while keeping inaccessible schemas on OID-based privilege checks that do not resolve hidden names. Require the current SELECT privilege result in both visible and hidden paths so stale snapshots do not keep exposing metadata after relation drops or SELECT revokes.
Closes #6038
Closes https://github.com/postgis/postgis/pull/915
diff --git a/NEWS b/NEWS
index 0190bff69..c6231dfc7 100644
--- a/NEWS
+++ b/NEWS
@@ -69,6 +69,8 @@ To take advantage of all postgis_sfcgal extension features SFCGAL 2.3+ is needed
(Darafei Praliaskouski)
- #3103, Add regression coverage for exact-schema find_srid and
geometry_columns lookups (Darafei Praliaskouski)
+ - #6038, Avoid stale relation lookups in geometry_columns after topology
+ objects are dropped (Darafei Praliaskouski)
- #5655, [doc] Skip XML validation during `make check` when xsltproc is
unavailable (Darafei Praliaskouski)
- #5487, Improve error detail for repeated upgrades after an incomplete
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 3647429c3..8167cbe72 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -6521,7 +6521,40 @@ SELECT current_database()::character varying(256) AS f_table_catalog,
) sr ON sr.connamespace = n.oid AND sr.conrelid = c.oid AND (a.attnum = ANY (sr.conkey))
WHERE (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'm'::"char", 'f'::"char", 'p'::"char"]))
AND NOT c.relname = 'raster_columns'::name AND t.typname = 'geometry'::name
- AND NOT pg_is_other_temp_schema(c.relnamespace) AND has_table_privilege(c.oid, 'SELECT'::text);
+ AND NOT pg_is_other_temp_schema(c.relnamespace)
+ -- A stale MVCC snapshot can still see a dropped pg_class row (#6038).
+ -- Re-resolve the qualified relation name only when the schema is visible,
+ -- because to_regclass reports permission errors for inaccessible schemas.
+ -- Without schema visibility, keep the old OID-based privilege semantics.
+ -- Keep the visible-schema SELECT check in the same CASE branch as the
+ -- re-resolution guard so PostgreSQL cannot evaluate it for a stale OID.
+ AND CASE WHEN has_schema_privilege(c.relnamespace, 'USAGE'::text)
+ THEN CASE WHEN pg_catalog.to_regclass(pg_catalog.format('%I.%I', n.nspname, c.relname)) = c.oid
+ THEN pg_catalog.has_table_privilege(c.oid, 'SELECT')
+ ELSE false
+ END
+ ELSE pg_catalog.has_table_privilege(c.oid, 'SELECT')
+ END
+ AND (
+ EXISTS (
+ SELECT 1
+ FROM pg_roles
+ WHERE rolname = current_user
+ AND rolsuper
+ )
+ OR EXISTS (
+ SELECT 1
+ FROM pg_roles
+ WHERE rolname = 'pg_read_all_data'
+ AND pg_has_role(current_user, oid, 'USAGE')
+ )
+ OR EXISTS (
+ SELECT 1
+ FROM aclexplode(COALESCE(c.relacl, acldefault('r', c.relowner))) AS acl
+ WHERE acl.privilege_type = 'SELECT'
+ AND (acl.grantee = 0 OR pg_has_role(acl.grantee, 'USAGE'))
+ )
+ );
-- TODO: support RETURNING and raise a WARNING
CREATE OR REPLACE RULE geometry_columns_insert AS
diff --git a/regress/core/regress_management.sql b/regress/core/regress_management.sql
index c92b7aad0..2b5027bd4 100644
--- a/regress/core/regress_management.sql
+++ b/regress/core/regress_management.sql
@@ -4,5 +4,65 @@ SET client_min_messages TO warning;
CREATE TABLE test_pt(gid SERIAL PRIMARY KEY, geom geometry);
INSERT INTO test_pt(geom) VALUES(ST_GeomFromEWKT('SRID=4326;POINT M(1 2 3)'));
SELECT populate_geometry_columns('test_pt'::regclass);
+SELECT '#6038.before', srid FROM geometry_columns WHERE f_table_schema = 'public' AND f_table_name = 'test_pt' AND f_geometry_column = 'geom';
SELECT 'The result: ' || DropGeometryTable('test_pt');
+SELECT '#6038.after', count(*) FROM geometry_columns WHERE f_table_schema = 'public' AND f_table_name = 'test_pt' AND f_geometry_column = 'geom';
+DO $$
+DECLARE
+ can_switch_role boolean;
+BEGIN
+ SELECT rolsuper INTO can_switch_role
+ FROM pg_roles
+ WHERE rolname = current_user;
+
+ IF can_switch_role THEN
+ DROP SCHEMA IF EXISTS test6038_private CASCADE;
+ DROP TABLE IF EXISTS public.test6038_visible_geom;
+ DROP ROLE IF EXISTS test6038_invisible;
+ DROP ROLE IF EXISTS test6038_visible;
+ CREATE ROLE test6038_invisible;
+ CREATE ROLE test6038_visible;
+ GRANT test6038_invisible TO CURRENT_USER;
+ GRANT test6038_visible TO CURRENT_USER;
+ CREATE TABLE public.test6038_visible_geom(geom geometry(Point, 4326));
+ GRANT SELECT ON public.test6038_visible_geom TO test6038_visible;
+ CREATE SCHEMA test6038_private;
+ CREATE TABLE test6038_private.hidden_geom(geom geometry(Point, 4326));
+ REVOKE ALL ON SCHEMA test6038_private FROM PUBLIC;
+ GRANT SELECT ON test6038_private.hidden_geom TO test6038_invisible;
+
+ EXECUTE 'SET LOCAL ROLE test6038_visible';
+ IF 1 != (SELECT count(*) FROM geometry_columns WHERE f_table_schema = 'public' AND f_table_name = 'test6038_visible_geom') THEN
+ RAISE EXCEPTION 'geometry_columns did not expose currently selectable table in visible schema';
+ END IF;
+ EXECUTE 'RESET ROLE';
+
+ REVOKE SELECT ON public.test6038_visible_geom FROM test6038_visible;
+ EXECUTE 'SET LOCAL ROLE test6038_visible';
+ IF 0 != (SELECT count(*) FROM geometry_columns WHERE f_table_schema = 'public' AND f_table_name = 'test6038_visible_geom') THEN
+ RAISE EXCEPTION 'geometry_columns exposed metadata after SELECT was revoked in visible schema';
+ END IF;
+ EXECUTE 'RESET ROLE';
+
+ -- The view must not resolve names inside schemas hidden from the caller.
+ EXECUTE 'SET LOCAL ROLE test6038_invisible';
+ IF 1 != (SELECT count(*) FROM geometry_columns WHERE f_table_schema = 'test6038_private') THEN
+ RAISE EXCEPTION 'geometry_columns did not preserve OID-based SELECT visibility in hidden schema';
+ END IF;
+ EXECUTE 'RESET ROLE';
+
+ REVOKE SELECT ON test6038_private.hidden_geom FROM test6038_invisible;
+ EXECUTE 'SET LOCAL ROLE test6038_invisible';
+ IF 0 != (SELECT count(*) FROM geometry_columns WHERE f_table_schema = 'test6038_private') THEN
+ RAISE EXCEPTION 'geometry_columns exposed hidden-schema metadata after SELECT was revoked';
+ END IF;
+ EXECUTE 'RESET ROLE';
+
+ DROP SCHEMA test6038_private CASCADE;
+ DROP TABLE public.test6038_visible_geom;
+ DROP ROLE test6038_invisible;
+ DROP ROLE test6038_visible;
+ END IF;
+END
+$$;
SELECT 'Unexistant: ' || DropGeometryTable('unexistent'); -- see ticket #861
diff --git a/regress/core/regress_management_expected b/regress/core/regress_management_expected
index e27f7f0ef..ac137e244 100644
--- a/regress/core/regress_management_expected
+++ b/regress/core/regress_management_expected
@@ -1,3 +1,5 @@
1
+#6038.before|4326
The result: public.test_pt dropped.
+#6038.after|0
Unexistant: public.unexistent dropped.
-----------------------------------------------------------------------
Summary of changes:
NEWS | 2 ++
postgis/postgis.sql.in | 35 ++++++++++++++++++-
regress/core/regress_management.sql | 60 ++++++++++++++++++++++++++++++++
regress/core/regress_management_expected | 2 ++
4 files changed, 98 insertions(+), 1 deletion(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list