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();

Reply via email to