This is an automated email from the ASF dual-hosted git repository.

avamingli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git


The following commit(s) were added to refs/heads/main by this push:
     new 2cc5674ea6e Cherry-pick of CVE fix: Fix privilege checks in 
pg_stats_ext and pg_stats_ext_exprs. (#1551)
2cc5674ea6e is described below

commit 2cc5674ea6e3a5cc85ff9101daf08b8dee5d533e
Author: reshke <[email protected]>
AuthorDate: Wed Feb 4 08:03:25 2026 +0500

    Cherry-pick of CVE fix: Fix privilege checks in pg_stats_ext and 
pg_stats_ext_exprs. (#1551)
    
    This is
    
https://github.com/postgres/postgres/commit/c3425383ba67ae6ecaddc8896025a91faadb430a
 commit, applied to Cloudberry. There was no issues in apply, only changes are 
to gporca expected output
    
    original commit message follows
    
    ===
    
    The catalog view pg_stats_ext fails to consider privileges for expression 
statistics.  The catalog view pg_stats_ext_exprs fails to consider privileges 
and row-level security policies.  To fix, restrict the data in these views to 
table owners or roles that inherit privileges of the table owner.  It may be 
possible to apply less restrictive privilege checks in some cases, but that is 
left as a future exercise.  Furthermore, for pg_stats_ext_exprs, do not return 
data for tables with row [...]
    
    On the back-branches, a fix-CVE-2024-4317.sql script is provided that will 
install into the "share" directory.  This file can be used to apply the fix to 
existing clusters.
    
    Bumps catversion on 'master' branch only.
    
    Reported-by: Lukas Fittl
    Reviewed-by: Noah Misch, Tomas Vondra, Tom Lane
    Security: CVE-2024-4317
    Backpatch-through: 14
---
 doc/src/sgml/catalogs.sgml                        |   7 +-
 src/backend/catalog/Makefile                      |   3 +-
 src/backend/catalog/fix-CVE-2024-4317.sql         | 115 ++++++++++++++++++++++
 src/backend/catalog/system_views.sql              |  11 +--
 src/test/regress/expected/rules.out               |   8 +-
 src/test/regress/expected/stats_ext.out           |  43 ++++++++
 src/test/regress/expected/stats_ext_optimizer.out |  43 ++++++++
 src/test/regress/sql/stats_ext.sql                |  27 +++++
 8 files changed, 240 insertions(+), 17 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 27b1f16b6a8..533856b12d7 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7480,8 +7480,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration 
count&gt;</replaceable>:<replaceable>&l
    is a publicly readable view
    on <structname>pg_statistic_ext_data</structname> (after joining
    with <link 
linkend="catalog-pg-statistic-ext"><structname>pg_statistic_ext</structname></link>)
 that only exposes
-   information about those tables and columns that are readable by the
-   current user.
+   information about tables the current user owns.
   </para>
 
   <table>
@@ -12925,7 +12924,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts 
ppx
    and <link 
linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link>
    catalogs.  This view allows access only to rows of
    <link 
linkend="catalog-pg-statistic-ext"><structname>pg_statistic_ext</structname></link>
 and <link 
linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link>
-   that correspond to tables the user has permission to read, and therefore
+   that correspond to tables the user owns, and therefore
    it is safe to allow public read access to this view.
   </para>
 
@@ -13125,7 +13124,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts 
ppx
    and <link 
linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link>
    catalogs.  This view allows access only to rows of
    <link 
linkend="catalog-pg-statistic-ext"><structname>pg_statistic_ext</structname></link>
 and <link 
linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link>
-   that correspond to tables the user has permission to read, and therefore
+   that correspond to tables the user owns, and therefore
    it is safe to allow public read access to this view.
   </para>
 
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index f1a0251e1cb..7010ee132ef 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -205,6 +205,7 @@ endif
        $(INSTALL_DATA) $(srcdir)/information_schema.sql 
'$(DESTDIR)$(datadir)/information_schema.sql'
        $(INSTALL_DATA) $(call vpathsearch,cdb_schema.sql) 
'$(DESTDIR)$(datadir)/cdb_init.d/cdb_schema.sql'
        $(INSTALL_DATA) $(srcdir)/sql_features.txt 
'$(DESTDIR)$(datadir)/sql_features.txt'
+       $(INSTALL_DATA) $(srcdir)/fix-CVE-2024-4317.sql 
'$(DESTDIR)$(datadir)/fix-CVE-2024-4317.sql'
 
 installdirs:
        $(MKDIR_P) '$(DESTDIR)$(datadir)'
@@ -212,7 +213,7 @@ installdirs:
 
 .PHONY: uninstall-data
 uninstall-data:
-       rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki 
system_constraints.sql system_functions.sql system_views.sql 
system_views_gp_summary.sql information_schema.sql cdb_init.d/cdb_schema.sql 
cdb_init.d/gp_toolkit.sql sql_features.txt)
+       rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki 
system_constraints.sql system_functions.sql system_views.sql 
system_views_gp_summary.sql information_schema.sql cdb_init.d/cdb_schema.sql 
cdb_init.d/gp_toolkit.sql sql_features.txt fix-CVE-2024-4317.sql)
 ifeq ($(USE_INTERNAL_FTS_FOUND), false)
        rm -f $(addprefix '$(DESTDIR)$(datadir)'/, external_fts.sql)
 endif
diff --git a/src/backend/catalog/fix-CVE-2024-4317.sql 
b/src/backend/catalog/fix-CVE-2024-4317.sql
new file mode 100644
index 00000000000..9e78c44c410
--- /dev/null
+++ b/src/backend/catalog/fix-CVE-2024-4317.sql
@@ -0,0 +1,115 @@
+/*
+ * fix-CVE-2024-4317.sql
+ *
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/fix-CVE-2024-4317.sql
+ *
+ * This file should be run in every database in the cluster to address
+ * CVE-2024-4317.
+ */
+
+SET search_path = pg_catalog;
+
+CREATE OR REPLACE VIEW pg_stats_ext WITH (security_barrier) AS
+    SELECT cn.nspname AS schemaname,
+           c.relname AS tablename,
+           sn.nspname AS statistics_schemaname,
+           s.stxname AS statistics_name,
+           pg_get_userbyid(s.stxowner) AS statistics_owner,
+           ( SELECT array_agg(a.attname ORDER BY a.attnum)
+             FROM unnest(s.stxkeys) k
+                  JOIN pg_attribute a
+                       ON (a.attrelid = s.stxrelid AND a.attnum = k)
+           ) AS attnames,
+           pg_get_statisticsobjdef_expressions(s.oid) as exprs,
+           s.stxkind AS kinds,
+           sd.stxdndistinct AS n_distinct,
+           sd.stxddependencies AS dependencies,
+           m.most_common_vals,
+           m.most_common_val_nulls,
+           m.most_common_freqs,
+           m.most_common_base_freqs
+    FROM pg_statistic_ext s JOIN pg_class c ON (c.oid = s.stxrelid)
+         JOIN pg_statistic_ext_data sd ON (s.oid = sd.stxoid)
+         LEFT JOIN pg_namespace cn ON (cn.oid = c.relnamespace)
+         LEFT JOIN pg_namespace sn ON (sn.oid = s.stxnamespace)
+         LEFT JOIN LATERAL
+                   ( SELECT array_agg(values) AS most_common_vals,
+                            array_agg(nulls) AS most_common_val_nulls,
+                            array_agg(frequency) AS most_common_freqs,
+                            array_agg(base_frequency) AS most_common_base_freqs
+                     FROM pg_mcv_list_items(sd.stxdmcv)
+                   ) m ON sd.stxdmcv IS NOT NULL
+    WHERE pg_has_role(c.relowner, 'USAGE')
+    AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
+
+CREATE OR REPLACE VIEW pg_stats_ext_exprs WITH (security_barrier) AS
+    SELECT cn.nspname AS schemaname,
+           c.relname AS tablename,
+           sn.nspname AS statistics_schemaname,
+           s.stxname AS statistics_name,
+           pg_get_userbyid(s.stxowner) AS statistics_owner,
+           stat.expr,
+           (stat.a).stanullfrac AS null_frac,
+           (stat.a).stawidth AS avg_width,
+           (stat.a).stadistinct AS n_distinct,
+           (CASE
+               WHEN (stat.a).stakind1 = 1 THEN (stat.a).stavalues1
+               WHEN (stat.a).stakind2 = 1 THEN (stat.a).stavalues2
+               WHEN (stat.a).stakind3 = 1 THEN (stat.a).stavalues3
+               WHEN (stat.a).stakind4 = 1 THEN (stat.a).stavalues4
+               WHEN (stat.a).stakind5 = 1 THEN (stat.a).stavalues5
+           END) AS most_common_vals,
+           (CASE
+               WHEN (stat.a).stakind1 = 1 THEN (stat.a).stanumbers1
+               WHEN (stat.a).stakind2 = 1 THEN (stat.a).stanumbers2
+               WHEN (stat.a).stakind3 = 1 THEN (stat.a).stanumbers3
+               WHEN (stat.a).stakind4 = 1 THEN (stat.a).stanumbers4
+               WHEN (stat.a).stakind5 = 1 THEN (stat.a).stanumbers5
+           END) AS most_common_freqs,
+           (CASE
+               WHEN (stat.a).stakind1 = 2 THEN (stat.a).stavalues1
+               WHEN (stat.a).stakind2 = 2 THEN (stat.a).stavalues2
+               WHEN (stat.a).stakind3 = 2 THEN (stat.a).stavalues3
+               WHEN (stat.a).stakind4 = 2 THEN (stat.a).stavalues4
+               WHEN (stat.a).stakind5 = 2 THEN (stat.a).stavalues5
+           END) AS histogram_bounds,
+           (CASE
+               WHEN (stat.a).stakind1 = 3 THEN (stat.a).stanumbers1[1]
+               WHEN (stat.a).stakind2 = 3 THEN (stat.a).stanumbers2[1]
+               WHEN (stat.a).stakind3 = 3 THEN (stat.a).stanumbers3[1]
+               WHEN (stat.a).stakind4 = 3 THEN (stat.a).stanumbers4[1]
+               WHEN (stat.a).stakind5 = 3 THEN (stat.a).stanumbers5[1]
+           END) correlation,
+           (CASE
+               WHEN (stat.a).stakind1 = 4 THEN (stat.a).stavalues1
+               WHEN (stat.a).stakind2 = 4 THEN (stat.a).stavalues2
+               WHEN (stat.a).stakind3 = 4 THEN (stat.a).stavalues3
+               WHEN (stat.a).stakind4 = 4 THEN (stat.a).stavalues4
+               WHEN (stat.a).stakind5 = 4 THEN (stat.a).stavalues5
+           END) AS most_common_elems,
+           (CASE
+               WHEN (stat.a).stakind1 = 4 THEN (stat.a).stanumbers1
+               WHEN (stat.a).stakind2 = 4 THEN (stat.a).stanumbers2
+               WHEN (stat.a).stakind3 = 4 THEN (stat.a).stanumbers3
+               WHEN (stat.a).stakind4 = 4 THEN (stat.a).stanumbers4
+               WHEN (stat.a).stakind5 = 4 THEN (stat.a).stanumbers5
+           END) AS most_common_elem_freqs,
+           (CASE
+               WHEN (stat.a).stakind1 = 5 THEN (stat.a).stanumbers1
+               WHEN (stat.a).stakind2 = 5 THEN (stat.a).stanumbers2
+               WHEN (stat.a).stakind3 = 5 THEN (stat.a).stanumbers3
+               WHEN (stat.a).stakind4 = 5 THEN (stat.a).stanumbers4
+               WHEN (stat.a).stakind5 = 5 THEN (stat.a).stanumbers5
+           END) AS elem_count_histogram
+    FROM pg_statistic_ext s JOIN pg_class c ON (c.oid = s.stxrelid)
+         LEFT JOIN pg_statistic_ext_data sd ON (s.oid = sd.stxoid)
+         LEFT JOIN pg_namespace cn ON (cn.oid = c.relnamespace)
+         LEFT JOIN pg_namespace sn ON (sn.oid = s.stxnamespace)
+         JOIN LATERAL (
+             SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
+                    unnest(sd.stxdexpr)::pg_statistic AS a
+         ) stat ON (stat.expr IS NOT NULL)
+    WHERE pg_has_role(c.relowner, 'USAGE')
+    AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
index 23a68ab22c6..ffd5a8a1207 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -310,12 +310,7 @@ CREATE VIEW pg_stats_ext WITH (security_barrier) AS
                             array_agg(base_frequency) AS most_common_base_freqs
                      FROM pg_mcv_list_items(sd.stxdmcv)
                    ) m ON sd.stxdmcv IS NOT NULL
-    WHERE NOT EXISTS
-              ( SELECT 1
-                FROM unnest(stxkeys) k
-                     JOIN pg_attribute a
-                          ON (a.attrelid = s.stxrelid AND a.attnum = k)
-                WHERE NOT has_column_privilege(c.oid, a.attnum, 'select') )
+    WHERE pg_has_role(c.relowner, 'USAGE')
     AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
 
 CREATE VIEW pg_stats_ext_exprs WITH (security_barrier) AS
@@ -384,7 +379,9 @@ CREATE VIEW pg_stats_ext_exprs WITH (security_barrier) AS
          JOIN LATERAL (
              SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
                     unnest(sd.stxdexpr)::pg_statistic AS a
-         ) stat ON (stat.expr IS NOT NULL);
+         ) stat ON (stat.expr IS NOT NULL)
+    WHERE pg_has_role(c.relowner, 'USAGE')
+    AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
 
 -- unprivileged users may read pg_statistic_ext but not pg_statistic_ext_data
 REVOKE ALL ON pg_statistic_ext_data FROM public;
diff --git a/src/test/regress/expected/rules.out 
b/src/test/regress/expected/rules.out
index cf2f24a5a67..16a471245a9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2439,10 +2439,7 @@ pg_stats_ext| SELECT cn.nspname AS schemaname,
             array_agg(pg_mcv_list_items.frequency) AS most_common_freqs,
             array_agg(pg_mcv_list_items.base_frequency) AS 
most_common_base_freqs
            FROM pg_mcv_list_items(sd.stxdmcv) pg_mcv_list_items(index, 
"values", nulls, frequency, base_frequency)) m ON ((sd.stxdmcv IS NOT NULL)))
-  WHERE ((NOT (EXISTS ( SELECT 1
-           FROM (unnest(s.stxkeys) k(k)
-             JOIN pg_attribute a ON (((a.attrelid = s.stxrelid) AND (a.attnum 
= k.k))))
-          WHERE (NOT has_column_privilege(c.oid, a.attnum, 'select'::text))))) 
AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+  WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = 
false) OR (NOT row_security_active(c.oid))));
 pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
     c.relname AS tablename,
     sn.nspname AS statistics_schemaname,
@@ -2514,7 +2511,8 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
      LEFT JOIN pg_namespace cn ON ((cn.oid = c.relnamespace)))
      LEFT JOIN pg_namespace sn ON ((sn.oid = s.stxnamespace)))
      JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) 
AS expr,
-            unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)));
+            unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
+  WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = 
false) OR (NOT row_security_active(c.oid))));
 pg_tables| SELECT n.nspname AS schemaname,
     c.relname AS tablename,
     pg_get_userbyid(c.relowner) AS tableowner,
diff --git a/src/test/regress/expected/stats_ext.out 
b/src/test/regress/expected/stats_ext.out
index b752abfc4c6..06aff4d5bd0 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -3235,10 +3235,53 @@ SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND 
b <<< 0; -- Should not le
 (0 rows)
 
 DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not 
leak
+-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
+RESET SESSION AUTHORIZATION;
+CREATE TABLE stats_ext_tbl (id INT PRIMARY KEY GENERATED BY DEFAULT AS 
IDENTITY, col TEXT);
+INSERT INTO stats_ext_tbl (col) VALUES ('secret'), ('secret'), ('very secret');
+CREATE STATISTICS s_col ON id, col FROM stats_ext_tbl;
+CREATE STATISTICS s_expr ON mod(id, 2), lower(col) FROM stats_ext_tbl;
+ANALYZE stats_ext_tbl;
+-- unprivileged role should not have access
+SET SESSION AUTHORIZATION regress_stats_user1;
+SELECT statistics_name, most_common_vals FROM pg_stats_ext x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name | most_common_vals 
+-----------------+------------------
+(0 rows)
+
+SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name | most_common_vals 
+-----------------+------------------
+(0 rows)
+
+-- give unprivileged role ownership of table
+RESET SESSION AUTHORIZATION;
+ALTER TABLE stats_ext_tbl OWNER TO regress_stats_user1;
+-- unprivileged role should now have access
+SET SESSION AUTHORIZATION regress_stats_user1;
+SELECT statistics_name, most_common_vals FROM pg_stats_ext x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name |             most_common_vals              
+-----------------+-------------------------------------------
+ s_col           | {{1,secret},{2,secret},{3,"very secret"}}
+ s_expr          | {{0,secret},{1,secret},{1,"very secret"}}
+(2 rows)
+
+SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name | most_common_vals 
+-----------------+------------------
+ s_expr          | {secret}
+ s_expr          | {1}
+(2 rows)
+
 -- Tidy up
 DROP OPERATOR <<< (int, int);
 DROP FUNCTION op_leak(int, int);
 RESET SESSION AUTHORIZATION;
+DROP TABLE stats_ext_tbl;
 DROP SCHEMA tststats CASCADE;
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table tststats.priv_test_tbl
diff --git a/src/test/regress/expected/stats_ext_optimizer.out 
b/src/test/regress/expected/stats_ext_optimizer.out
index 9f1b78af0b3..dafbf0a28b4 100644
--- a/src/test/regress/expected/stats_ext_optimizer.out
+++ b/src/test/regress/expected/stats_ext_optimizer.out
@@ -3270,10 +3270,53 @@ SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND 
b <<< 0; -- Should not le
 (0 rows)
 
 DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not 
leak
+-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
+RESET SESSION AUTHORIZATION;
+CREATE TABLE stats_ext_tbl (id INT PRIMARY KEY GENERATED BY DEFAULT AS 
IDENTITY, col TEXT);
+INSERT INTO stats_ext_tbl (col) VALUES ('secret'), ('secret'), ('very secret');
+CREATE STATISTICS s_col ON id, col FROM stats_ext_tbl;
+CREATE STATISTICS s_expr ON mod(id, 2), lower(col) FROM stats_ext_tbl;
+ANALYZE stats_ext_tbl;
+-- unprivileged role should not have access
+SET SESSION AUTHORIZATION regress_stats_user1;
+SELECT statistics_name, most_common_vals FROM pg_stats_ext x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name | most_common_vals 
+-----------------+------------------
+(0 rows)
+
+SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name | most_common_vals 
+-----------------+------------------
+(0 rows)
+
+-- give unprivileged role ownership of table
+RESET SESSION AUTHORIZATION;
+ALTER TABLE stats_ext_tbl OWNER TO regress_stats_user1;
+-- unprivileged role should now have access
+SET SESSION AUTHORIZATION regress_stats_user1;
+SELECT statistics_name, most_common_vals FROM pg_stats_ext x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name |             most_common_vals              
+-----------------+-------------------------------------------
+ s_col           | {{1,secret},{2,secret},{3,"very secret"}}
+ s_expr          | {{0,secret},{1,secret},{1,"very secret"}}
+(2 rows)
+
+SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+ statistics_name | most_common_vals 
+-----------------+------------------
+ s_expr          | {secret}
+ s_expr          | {1}
+(2 rows)
+
 -- Tidy up
 DROP OPERATOR <<< (int, int);
 DROP FUNCTION op_leak(int, int);
 RESET SESSION AUTHORIZATION;
+DROP TABLE stats_ext_tbl;
 DROP SCHEMA tststats CASCADE;
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table tststats.priv_test_tbl
diff --git a/src/test/regress/sql/stats_ext.sql 
b/src/test/regress/sql/stats_ext.sql
index 6840818118d..744bb00c161 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -1649,10 +1649,37 @@ SET SESSION AUTHORIZATION regress_stats_user1;
 SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not 
leak
 DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not 
leak
 
+-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
+RESET SESSION AUTHORIZATION;
+CREATE TABLE stats_ext_tbl (id INT PRIMARY KEY GENERATED BY DEFAULT AS 
IDENTITY, col TEXT);
+INSERT INTO stats_ext_tbl (col) VALUES ('secret'), ('secret'), ('very secret');
+CREATE STATISTICS s_col ON id, col FROM stats_ext_tbl;
+CREATE STATISTICS s_expr ON mod(id, 2), lower(col) FROM stats_ext_tbl;
+ANALYZE stats_ext_tbl;
+
+-- unprivileged role should not have access
+SET SESSION AUTHORIZATION regress_stats_user1;
+SELECT statistics_name, most_common_vals FROM pg_stats_ext x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+
+-- give unprivileged role ownership of table
+RESET SESSION AUTHORIZATION;
+ALTER TABLE stats_ext_tbl OWNER TO regress_stats_user1;
+
+-- unprivileged role should now have access
+SET SESSION AUTHORIZATION regress_stats_user1;
+SELECT statistics_name, most_common_vals FROM pg_stats_ext x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x
+    WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*);
+
 -- Tidy up
 DROP OPERATOR <<< (int, int);
 DROP FUNCTION op_leak(int, int);
 RESET SESSION AUTHORIZATION;
+DROP TABLE stats_ext_tbl;
 DROP SCHEMA tststats CASCADE;
 DROP USER regress_stats_user1;
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to