diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 655bdced4f1..1920d626d34 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -707,6 +707,17 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft_empty ORDER BY c1;
Remote SQL: SELECT c1, c2 FROM public.loct_empty ORDER BY c1 ASC NULLS LAST
(3 rows)
+-- test restriction on non-system foreign tables.
+SET restrict_nonsystem_relation_kind TO 'foreign-table';
+SELECT * from ft1 where c1 < 1; -- ERROR
+ERROR: access to non-system foreign table is restricted
+INSERT INTO ft1 (c1) VALUES (1); -- ERROR
+ERROR: access to non-system foreign table is restricted
+DELETE FROM ft1 WHERE c1 = 1; -- ERROR
+ERROR: access to non-system foreign table is restricted
+TRUNCATE ft1; -- ERROR
+ERROR: access to non-system foreign table is restricted
+RESET restrict_nonsystem_relation_kind;
-- ===================================================================
-- WHERE with remotely-executable conditions
-- ===================================================================
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 724c1802b28..97ac09c1973 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -321,6 +321,14 @@ DELETE FROM loct_empty;
ANALYZE ft_empty;
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft_empty ORDER BY c1;
+-- test restriction on non-system foreign tables.
+SET restrict_nonsystem_relation_kind TO 'foreign-table';
+SELECT * from ft1 where c1 < 1; -- ERROR
+INSERT INTO ft1 (c1) VALUES (1); -- ERROR
+DELETE FROM ft1 WHERE c1 = 1; -- ERROR
+TRUNCATE ft1; -- ERROR
+RESET restrict_nonsystem_relation_kind;
+
-- ===================================================================
-- WHERE with remotely-executable conditions
-- ===================================================================
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index bc3d0d1bd14..765ee308df6 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9012,6 +9012,23 @@ SET XML OPTION { DOCUMENT | CONTENT };
+
+ restrict_nonsystem_relation_kind (string)
+
+ restrict_nonsystem_relation_kind
+ configuration parameter
+
+
+
+
+ This variable specifies relation kind to which access is restricted.
+ It contains a comma-separated list of relation kind. Currently, the
+ supported relation kinds are view and
+ foreign-table.
+
+
+
+
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 956f97e2537..c2c6d36647e 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -819,6 +819,14 @@ PostgreSQL documentation
The only exception is that an empty pattern is disallowed.
+
+
+ Using wildcards in may result
+ in access to unexpected foreign servers. Also, to use this option securely,
+ make sure that the named server must have a trusted owner.
+
+
+
When is specified,
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 2d60eff9459..7a00c5f06db 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -33,6 +33,7 @@
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -590,6 +591,15 @@ GetFdwRoutine(Oid fdwhandler)
Datum datum;
FdwRoutine *routine;
+ /* Check if the access to foreign tables is restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
+ {
+ /* there must not be built-in FDW handler */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system foreign table is restricted")));
+ }
+
datum = OidFunctionCall0(fdwhandler);
routine = (FdwRoutine *) DatumGetPointer(datum);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 53c1bf7d338..20c1587b062 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -47,6 +47,7 @@
#include "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
+#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"
#include "utils/uri.h"
@@ -8482,7 +8483,19 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
Assert(rte->rtekind == RTE_RELATION);
if (rte->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ /* Check if the access to foreign tables is restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
+ {
+ /* there must not be built-in foreign tables */
+ Assert(rte->relid >= FirstNormalObjectId);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system foreign table is restricted")));
+ }
+
fdwroutine = GetFdwRoutineByRelId(rte->relid);
+ }
else
fdwroutine = NULL;
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 5417db71431..13c2ea77699 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -49,6 +49,7 @@
#include "rewrite/rewriteManip.h"
#include "statistics/statistics.h"
#include "storage/bufmgr.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
@@ -468,6 +469,17 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* Grab foreign-table info using the relcache, while we have it */
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{
+ /* Check if the access to foreign tables is restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
+ {
+ /* there must not be built-in foreign tables */
+ Assert(RelationGetRelid(relation) >= FirstNormalObjectId);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system foreign table is restricted")));
+ }
+
rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
rel->segSeverids = GetForeignServerSegsByRelId(RelationGetRelid(relation));
rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 48d1caace62..0f8500be9a0 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -42,6 +42,7 @@
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSearchCycle.h"
#include "rewrite/rowsecurity.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -1789,6 +1790,14 @@ ApplyRetrieveRule(Query *parsetree,
if (rule->qual != NULL)
elog(ERROR, "cannot handle qualified ON SELECT rule");
+ /* Check if the expansion of non-system views are restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
+ RelationGetRelid(relation) >= FirstNormalObjectId))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system view \"%s\" is restricted",
+ RelationGetRelationName(relation))));
+
if (rt_index == parsetree->resultRelation)
{
/*
@@ -3172,6 +3181,14 @@ rewriteTargetView(Query *parsetree, Relation view)
}
}
+ /* Check if the expansion of non-system views are restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
+ RelationGetRelid(view) >= FirstNormalObjectId))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system view \"%s\" is restricted",
+ RelationGetRelationName(view))));
+
/*
* For INSERT/UPDATE the modified columns must all be updatable. Note that
* we get the modified columns from the query's targetlist, not from the
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 62ded58aafb..fc35930f58c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -89,6 +89,7 @@
#include "utils/snapmgr.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
+#include "utils/varlena.h"
#include "cdb/cdbutil.h"
#include "cdb/cdbvars.h"
@@ -151,6 +152,8 @@ cancel_pending_hook_type cancel_pending_hook = NULL;
* Hook for query execution.
*/
exec_simple_query_hook_type exec_simple_query_hook = NULL;
+/* flags for non-system relation kinds to restrict use */
+int restrict_nonsystem_relation_kind;
/* ----------------
* private typedefs etc
@@ -4546,6 +4549,66 @@ assign_max_stack_depth(int newval, void *extra)
max_stack_depth_bytes = newval_bytes;
}
+/*
+ * GUC check_hook for restrict_nonsystem_relation_kind
+ */
+bool
+check_restrict_nonsystem_relation_kind(char **newval, void **extra, GucSource source)
+{
+ char *rawstring;
+ List *elemlist;
+ ListCell *l;
+ int flags = 0;
+
+ /* Need a modifiable copy of string */
+ rawstring = pstrdup(*newval);
+
+ if (!SplitIdentifierString(rawstring, ',', &elemlist))
+ {
+ /* syntax error in list */
+ GUC_check_errdetail("List syntax is invalid.");
+ pfree(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+
+ foreach(l, elemlist)
+ {
+ char *tok = (char *) lfirst(l);
+
+ if (pg_strcasecmp(tok, "view") == 0)
+ flags |= RESTRICT_RELKIND_VIEW;
+ else if (pg_strcasecmp(tok, "foreign-table") == 0)
+ flags |= RESTRICT_RELKIND_FOREIGN_TABLE;
+ else
+ {
+ GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
+ pfree(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+ }
+
+ pfree(rawstring);
+ list_free(elemlist);
+
+ /* Save the flags in *extra, for use by the assign function */
+ *extra = malloc(sizeof(int));
+ *((int *) *extra) = flags;
+
+ return true;
+}
+
+/*
+ * GUC assign_hook for restrict_nonsystem_relation_kind
+ */
+void
+assign_restrict_nonsystem_relation_kind(const char *newval, void *extra)
+{
+ int *flags = (int *) extra;
+
+ restrict_nonsystem_relation_kind = *flags;
+}
/*
* set_debug_options --- apply "-d N" command line option
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index cb3b1a1cbdd..354302afd54 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -686,6 +686,8 @@ static char *recovery_target_xid_string;
static char *recovery_target_name_string;
static char *recovery_target_lsn_string;
static char *file_encryption_method_str;
+static char *restrict_nonsystem_relation_kind_string;
+
/* should be static, but commands/variable.c needs to get at this */
char *role_string;
@@ -4761,7 +4763,18 @@ static struct config_string ConfigureNamesString[] =
"",
NULL, NULL, NULL
},
-
+
+ {
+ {"restrict_nonsystem_relation_kind", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets relation kinds of non-system relation to restrict use"),
+ NULL,
+ GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE
+ },
+ &restrict_nonsystem_relation_kind_string,
+ "",
+ check_restrict_nonsystem_relation_kind, assign_restrict_nonsystem_relation_kind, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7ea694ce07d..7630e596e5c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -362,6 +362,7 @@ static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
const char *prefix, Archive *fout);
static char *get_synchronized_snapshot(Archive *fout);
static void setupDumpWorker(Archive *AHX);
+static void set_restrict_relation_kind(Archive *AH, const char *value);
static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
static bool forcePartitionRootLoad(const TableInfo *tbinfo);
@@ -1447,6 +1448,13 @@ setup_connection(Archive *AH, const char *dumpencoding,
ExecuteSqlStatement(AH, "SET row_security = off");
}
+ /*
+ * For security reasons, we restrict the expansion of non-system views and
+ * access to foreign tables during the pg_dump process. This restriction
+ * is adjusted when dumping foreign table data.
+ */
+ set_restrict_relation_kind(AH, "view, foreign-table");
+
/*
* Start transaction-snapshot mode transaction to dump consistent data.
*/
@@ -2341,6 +2349,11 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
*/
if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
{
+ /* Temporary allows to access to foreign tables to dump data */
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ set_restrict_relation_kind(fout, "view");
+
+ /* Note: this syntax is only supported in 8.2 and up */
appendPQExpBufferStr(q, "COPY (SELECT ");
/* klugery to get rid of parens in column list */
if (strlen(column_list) > 2)
@@ -2452,6 +2465,11 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
classname);
destroyPQExpBuffer(q);
+
+ /* Revert back the setting */
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ set_restrict_relation_kind(fout, "view, foreign-table");
+
return 1;
}
@@ -2478,6 +2496,10 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
int rows_per_statement = dopt->dump_inserts;
int rows_this_statement = 0;
+ /* Temporary allows to access to foreign tables to dump data */
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ set_restrict_relation_kind(fout, "view");
+
/*
* If we're going to emit INSERTs with column names, the most efficient
* way to deal with generated columns is to exclude them entirely. For
@@ -2717,6 +2739,10 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
destroyPQExpBuffer(insertStmt);
free(attgenerated);
+ /* Revert back the setting */
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ set_restrict_relation_kind(fout, "view, foreign-table");
+
return 1;
}
@@ -4779,6 +4805,28 @@ is_superuser(Archive *fout)
return false;
}
+/*
+ * Set the given value to restrict_nonsystem_relation_kind value. Since
+ * restrict_nonsystem_relation_kind is introduced in minor version releases,
+ * the setting query is effective only where available.
+ */
+static void
+set_restrict_relation_kind(Archive *AH, const char *value)
+{
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+
+ appendPQExpBuffer(query,
+ "SELECT set_config(name, '%s', false) "
+ "FROM pg_settings "
+ "WHERE name = 'restrict_nonsystem_relation_kind'",
+ value);
+ res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+}
+
/*
* getSubscriptions
* get information about subscriptions
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 33c929e9082..c7c534417a2 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -47,6 +47,12 @@ typedef enum
extern PGDLLIMPORT int log_statement;
+/* Flags for restrict_nonsystem_relation_kind value */
+#define RESTRICT_RELKIND_VIEW 0x01
+#define RESTRICT_RELKIND_FOREIGN_TABLE 0x02
+
+extern PGDLLIMPORT int restrict_nonsystem_relation_kind;
+
extern List *pg_parse_query(const char *query_string);
extern List *pg_rewrite_query(Query *query);
extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
@@ -67,6 +73,9 @@ extern List *pg_plan_queries(List *querytrees, const char *query_string,
extern bool check_max_stack_depth(int *newval, void **extra, GucSource source);
extern void assign_max_stack_depth(int newval, void *extra);
+extern bool check_restrict_nonsystem_relation_kind(char **newval, void **extra,
+ GucSource source);
+extern void assign_restrict_nonsystem_relation_kind(const char *newval, void *extra);
extern void die(SIGNAL_ARGS);
extern void quickdie(SIGNAL_ARGS) pg_attribute_noreturn();
diff --git a/src/include/utils/unsync_guc_name.h b/src/include/utils/unsync_guc_name.h
index cba11770a81..55a81df5bae 100644
--- a/src/include/utils/unsync_guc_name.h
+++ b/src/include/utils/unsync_guc_name.h
@@ -537,6 +537,7 @@
"resource_select_only",
"restart_after_crash",
"restore_command",
+ "restrict_nonsystem_relation_kind",
"role",
"runaway_detector_activation_percent",
"segment_size",
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index fdb0657bb72..87984d48678 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1997,6 +1997,44 @@ select pg_get_viewdef('tt26v', true);
FROM ( VALUES (1,2,3)) v(x, y, z);
(1 row)
+-- Test that changing the relkind of a relcache entry doesn't cause
+-- trouble. Prior instances of where it did:
+-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com
+-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com
+CREATE TABLE tt26(c int);
+BEGIN;
+CREATE TABLE tt27(c int);
+SAVEPOINT q;
+CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
+SELECT * FROM tt27;
+ c
+---
+(0 rows)
+
+ROLLBACK TO q;
+CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
+ROLLBACK;
+BEGIN;
+CREATE TABLE tt28(c int);
+CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
+CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
+ERROR: "tt28" is already a view
+ROLLBACK;
+-- test restriction on non-system view expansion.
+create table tt27v_tbl (a int);
+create view tt27v as select a from tt27v_tbl;
+set restrict_nonsystem_relation_kind to 'view';
+select a from tt27v where a > 0; -- Error
+ERROR: access to non-system view "tt27v" is restricted
+insert into tt27v values (1); -- Error
+ERROR: access to non-system view "tt27v" is restricted
+select viewname from pg_views where viewname = 'tt27v'; -- Ok to access a system view.
+ viewname
+----------
+ tt27v
+(1 row)
+
+reset restrict_nonsystem_relation_kind;
-- test display negative operator of const-folder expression
create table tdis(a int, b int, c int);
NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table.
@@ -2044,7 +2082,7 @@ drop cascades to view aliased_view_2
drop cascades to view aliased_view_3
drop cascades to view aliased_view_4
DROP SCHEMA testviewschm2 CASCADE;
-NOTICE: drop cascades to 76 other objects
+NOTICE: drop cascades to 78 other objects
DETAIL: drop cascades to table t1
drop cascades to view temporal1
drop cascades to view temporal2
@@ -2119,5 +2157,8 @@ drop cascades to view tt23v
drop cascades to view tt24v
drop cascades to view tt25v
drop cascades to view tt26v
+drop cascades to table tt26
+drop cascades to table tt27v_tbl
+drop cascades to view tt27v
drop cascades to table tdis
drop cascades to view tdis_v1
diff --git a/src/test/regress/expected/create_view_optimizer.out b/src/test/regress/expected/create_view_optimizer.out
index 5719aea410c..9c5b5b8de01 100755
--- a/src/test/regress/expected/create_view_optimizer.out
+++ b/src/test/regress/expected/create_view_optimizer.out
@@ -1995,6 +1995,44 @@ select pg_get_viewdef('tt26v', true);
FROM ( VALUES (1,2,3)) v(x, y, z);
(1 row)
+-- Test that changing the relkind of a relcache entry doesn't cause
+-- trouble. Prior instances of where it did:
+-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com
+-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com
+CREATE TABLE tt26(c int);
+BEGIN;
+CREATE TABLE tt27(c int);
+SAVEPOINT q;
+CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
+SELECT * FROM tt27;
+ c
+---
+(0 rows)
+
+ROLLBACK TO q;
+CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
+ROLLBACK;
+BEGIN;
+CREATE TABLE tt28(c int);
+CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
+CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
+ERROR: "tt28" is already a view
+ROLLBACK;
+-- test restriction on non-system view expansion.
+create table tt27v_tbl (a int);
+create view tt27v as select a from tt27v_tbl;
+set restrict_nonsystem_relation_kind to 'view';
+select a from tt27v where a > 0; -- Error
+ERROR: access to non-system view "tt27v" is restricted
+insert into tt27v values (1); -- Error
+ERROR: access to non-system view "tt27v" is restricted
+select viewname from pg_views where viewname = 'tt27v'; -- Ok to access a system view.
+ viewname
+----------
+ tt27v
+(1 row)
+
+reset restrict_nonsystem_relation_kind;
-- test display negative operator of const-folder expression
create table tdis(a int, b int, c int);
NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table.
@@ -2042,7 +2080,7 @@ drop cascades to view aliased_view_2
drop cascades to view aliased_view_3
drop cascades to view aliased_view_4
DROP SCHEMA testviewschm2 CASCADE;
-NOTICE: drop cascades to 76 other objects
+NOTICE: drop cascades to 78 other objects
DETAIL: drop cascades to table t1
drop cascades to view temporal1
drop cascades to view temporal2
@@ -2117,5 +2155,8 @@ drop cascades to view tt23v
drop cascades to view tt24v
drop cascades to view tt25v
drop cascades to view tt26v
+drop cascades to table tt26
+drop cascades to table tt27v_tbl
+drop cascades to view tt27v
drop cascades to table tdis
drop cascades to view tdis_v1
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 2e7452ac9ea..fd0292aa210 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -692,6 +692,38 @@ select x + y + z as c1,
from (values(1,2,3)) v(x,y,z);
select pg_get_viewdef('tt26v', true);
+
+-- Test that changing the relkind of a relcache entry doesn't cause
+-- trouble. Prior instances of where it did:
+-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com
+-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com
+CREATE TABLE tt26(c int);
+
+BEGIN;
+CREATE TABLE tt27(c int);
+SAVEPOINT q;
+CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
+SELECT * FROM tt27;
+ROLLBACK TO q;
+CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
+ROLLBACK;
+
+BEGIN;
+CREATE TABLE tt28(c int);
+CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
+CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
+ROLLBACK;
+
+
+-- test restriction on non-system view expansion.
+create table tt27v_tbl (a int);
+create view tt27v as select a from tt27v_tbl;
+set restrict_nonsystem_relation_kind to 'view';
+select a from tt27v where a > 0; -- Error
+insert into tt27v values (1); -- Error
+select viewname from pg_views where viewname = 'tt27v'; -- Ok to access a system view.
+reset restrict_nonsystem_relation_kind;
+
-- test display negative operator of const-folder expression
create table tdis(a int, b int, c int);
create view tdis_v1 as select a,b,c, -1::int from tdis group by 1,2,3,4;