This is an automated email from the ASF dual-hosted git repository.
MuhammadTahaNaveed pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push:
new 6e073cf4 Fix utility commands when AGE is preloaded but not installed
(#2578)
6e073cf4 is described below
commit 6e073cf40917731b4cd5b2654e4a93da005a2295
Author: crdv7 <[email protected]>
AuthorDate: Sun Oct 11 14:04:24 2026 +0800
Fix utility commands when AGE is preloaded but not installed (#2578)
When AGE is loaded through shared_preload_libraries, TRUNCATE,
VACUUM FULL on a table, and CLUSTER on a table attempt to access
AGE catalogs even if the extension is not installed in the current
database. This fails with a missing ag_catalog schema or ag_label
table.
Check is_age_extension_exists() before these utility branches perform
graph-cache invalidation. If AGE is absent, continue through the normal
ProcessUtility chain. Preserve existing invalidation when AGE is
installed.
Add regression coverage for missing and empty ag_catalog schemas.
Verify data preservation after VACUUM FULL and CLUSTER, the selected
index's clustering flag, and an empty table after TRUNCATE.
Follow-up to 2438.
---
regress/expected/drop.out | 77 +++++++++++++++++++++++++++++++++++++++
regress/sql/drop.sql | 78 ++++++++++++++++++++++++++++++++++++++++
src/backend/catalog/ag_catalog.c | 15 ++++++++
3 files changed, 170 insertions(+)
diff --git a/regress/expected/drop.out b/regress/expected/drop.out
index 43e0bf41..61dc719d 100644
--- a/regress/expected/drop.out
+++ b/regress/expected/drop.out
@@ -48,6 +48,83 @@ DROP SCHEMA IF EXISTS ag_catalog CASCADE;
RESET client_min_messages;
CREATE SCHEMA _regress_drop;
DROP SCHEMA _regress_drop; -- should'n produce the ERROR
+-- A loaded library must not inspect AGE catalogs before installation.
+CREATE TABLE public._regress_utility (id int);
+CREATE INDEX _regress_utility_idx ON public._regress_utility (id);
+INSERT INTO public._regress_utility VALUES (2), (1), (2), (NULL);
+VACUUM FULL public._regress_utility;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'VACUUM FULL did not preserve the ordinary table data';
+ END IF;
+END
+$$;
+CLUSTER public._regress_utility USING _regress_utility_idx;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'CLUSTER did not preserve the ordinary table data';
+ END IF;
+ IF NOT EXISTS (
+ SELECT FROM pg_catalog.pg_index
+ WHERE indexrelid = 'public._regress_utility_idx'::regclass
+ AND indisclustered
+ ) THEN
+ RAISE EXCEPTION 'CLUSTER did not mark the specified index';
+ END IF;
+END
+$$;
+TRUNCATE public._regress_utility;
+DO $$
+BEGIN
+ IF EXISTS (SELECT FROM public._regress_utility) THEN
+ RAISE EXCEPTION 'TRUNCATE did not empty the ordinary table';
+ END IF;
+END
+$$;
+-- An empty schema is not an installed extension.
+CREATE SCHEMA ag_catalog;
+-- Clear the previous CLUSTER marker so this case tests it independently.
+ALTER TABLE public._regress_utility SET WITHOUT CLUSTER;
+INSERT INTO public._regress_utility VALUES (2), (1), (2), (NULL);
+VACUUM FULL public._regress_utility;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'VACUUM FULL did not preserve the ordinary table data';
+ END IF;
+END
+$$;
+CLUSTER public._regress_utility USING _regress_utility_idx;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'CLUSTER did not preserve the ordinary table data';
+ END IF;
+ IF NOT EXISTS (
+ SELECT FROM pg_catalog.pg_index
+ WHERE indexrelid = 'public._regress_utility_idx'::regclass
+ AND indisclustered
+ ) THEN
+ RAISE EXCEPTION 'CLUSTER did not mark the specified index';
+ END IF;
+END
+$$;
+TRUNCATE public._regress_utility;
+DO $$
+BEGIN
+ IF EXISTS (SELECT FROM public._regress_utility) THEN
+ RAISE EXCEPTION 'TRUNCATE did not empty the ordinary table';
+ END IF;
+END
+$$;
+DROP TABLE public._regress_utility;
+DROP SCHEMA ag_catalog;
-- Recreate the extension and validate we can recreate a graph
CREATE EXTENSION age;
SELECT create_graph('drop');
diff --git a/regress/sql/drop.sql b/regress/sql/drop.sql
index a71d5a74..006e50dd 100644
--- a/regress/sql/drop.sql
+++ b/regress/sql/drop.sql
@@ -37,6 +37,84 @@ RESET client_min_messages;
CREATE SCHEMA _regress_drop;
DROP SCHEMA _regress_drop; -- should'n produce the ERROR
+-- A loaded library must not inspect AGE catalogs before installation.
+CREATE TABLE public._regress_utility (id int);
+CREATE INDEX _regress_utility_idx ON public._regress_utility (id);
+INSERT INTO public._regress_utility VALUES (2), (1), (2), (NULL);
+VACUUM FULL public._regress_utility;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'VACUUM FULL did not preserve the ordinary table data';
+ END IF;
+END
+$$;
+CLUSTER public._regress_utility USING _regress_utility_idx;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'CLUSTER did not preserve the ordinary table data';
+ END IF;
+ IF NOT EXISTS (
+ SELECT FROM pg_catalog.pg_index
+ WHERE indexrelid = 'public._regress_utility_idx'::regclass
+ AND indisclustered
+ ) THEN
+ RAISE EXCEPTION 'CLUSTER did not mark the specified index';
+ END IF;
+END
+$$;
+TRUNCATE public._regress_utility;
+DO $$
+BEGIN
+ IF EXISTS (SELECT FROM public._regress_utility) THEN
+ RAISE EXCEPTION 'TRUNCATE did not empty the ordinary table';
+ END IF;
+END
+$$;
+-- An empty schema is not an installed extension.
+CREATE SCHEMA ag_catalog;
+-- Clear the previous CLUSTER marker so this case tests it independently.
+ALTER TABLE public._regress_utility SET WITHOUT CLUSTER;
+INSERT INTO public._regress_utility VALUES (2), (1), (2), (NULL);
+VACUUM FULL public._regress_utility;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'VACUUM FULL did not preserve the ordinary table data';
+ END IF;
+END
+$$;
+CLUSTER public._regress_utility USING _regress_utility_idx;
+DO $$
+BEGIN
+ IF (SELECT array_agg(id ORDER BY id) FROM public._regress_utility)
+ IS DISTINCT FROM ARRAY[1, 2, 2, NULL] THEN
+ RAISE EXCEPTION 'CLUSTER did not preserve the ordinary table data';
+ END IF;
+ IF NOT EXISTS (
+ SELECT FROM pg_catalog.pg_index
+ WHERE indexrelid = 'public._regress_utility_idx'::regclass
+ AND indisclustered
+ ) THEN
+ RAISE EXCEPTION 'CLUSTER did not mark the specified index';
+ END IF;
+END
+$$;
+TRUNCATE public._regress_utility;
+DO $$
+BEGIN
+ IF EXISTS (SELECT FROM public._regress_utility) THEN
+ RAISE EXCEPTION 'TRUNCATE did not empty the ordinary table';
+ END IF;
+END
+$$;
+DROP TABLE public._regress_utility;
+DROP SCHEMA ag_catalog;
+
-- Recreate the extension and validate we can recreate a graph
CREATE EXTENSION age;
diff --git a/src/backend/catalog/ag_catalog.c b/src/backend/catalog/ag_catalog.c
index 79e3fd45..c27ba839 100644
--- a/src/backend/catalog/ag_catalog.c
+++ b/src/backend/catalog/ag_catalog.c
@@ -237,6 +237,11 @@ void ag_ProcessUtility_hook(PlannedStmt *pstmt, const char
*queryString,
TruncateStmt *tstmt = (TruncateStmt *) parsetree;
ListCell *lc;
+ if (!is_age_extension_exists())
+ {
+ break;
+ }
+
foreach(lc, tstmt->relations)
{
RangeVar *rv = (RangeVar *) lfirst(lc);
@@ -268,6 +273,11 @@ void ag_ProcessUtility_hook(PlannedStmt *pstmt, const char
*queryString,
*/
VacuumStmt *vstmt = (VacuumStmt *) parsetree;
+ if (!is_age_extension_exists())
+ {
+ break;
+ }
+
if (vacuum_rewrites_heap(vstmt))
{
if (vstmt->rels == NIL)
@@ -294,6 +304,11 @@ void ag_ProcessUtility_hook(PlannedStmt *pstmt, const char
*queryString,
/* CLUSTER rewrites the heap for the same reason. */
ClusterStmt *cstmt = (ClusterStmt *) parsetree;
+ if (!is_age_extension_exists())
+ {
+ break;
+ }
+
if (cstmt->relation == NULL)
{
increment_all_graph_versions();