Fix comments and add handle INSERTs on pg_class correctly

pull/7577/head
Jelte Fennema-Nio 2024-04-16 17:05:32 +02:00
parent cbdf059c52
commit e588bbb224
3 changed files with 88 additions and 4 deletions

View File

@ -54,6 +54,7 @@ static bool ShouldHideShardsInternal(void);
static bool IsPgBgWorker(void); static bool IsPgBgWorker(void);
static bool FilterShardsFromPgclass(Node *node, void *context); static bool FilterShardsFromPgclass(Node *node, void *context);
static Node * CreateRelationIsAKnownShardFilter(int pgClassVarno); static Node * CreateRelationIsAKnownShardFilter(int pgClassVarno);
static bool HasRangeTableRef(Node *node, int *varno);
PG_FUNCTION_INFO_V1(citus_table_is_visible); PG_FUNCTION_INFO_V1(citus_table_is_visible);
PG_FUNCTION_INFO_V1(relation_is_a_known_shard); PG_FUNCTION_INFO_V1(relation_is_a_known_shard);
@ -421,8 +422,8 @@ IsPgBgWorker(void)
/* /*
* FilterShardsFromPgclass adds a NOT relation_is_a_known_shard(oid) filter * FilterShardsFromPgclass adds a "relation_is_a_known_shard(oid) IS NOT TRUE"
* to the security quals of pg_class RTEs. * filter to the quals of queries that query pg_class.
*/ */
static bool static bool
FilterShardsFromPgclass(Node *node, void *context) FilterShardsFromPgclass(Node *node, void *context)
@ -456,6 +457,17 @@ FilterShardsFromPgclass(Node *node, void *context)
continue; continue;
} }
/*
* Skip if pg_class is not actually queried. This is possible on
* INSERT statements that insert into pg_class.
*/
if (!expression_tree_walker((Node *) query->jointree->fromlist,
HasRangeTableRef, &varno))
{
/* the query references pg_class */
continue;
}
/* make sure the expression is in the right memory context */ /* make sure the expression is in the right memory context */
MemoryContext originalContext = MemoryContextSwitchTo(queryContext); MemoryContext originalContext = MemoryContextSwitchTo(queryContext);
@ -485,6 +497,23 @@ FilterShardsFromPgclass(Node *node, void *context)
} }
/*
* HasRangeTableRef passed to expression_tree_walker to check if a node is a
* RangeTblRef of the given varno is present in a fromlist.
*/
static bool
HasRangeTableRef(Node *node, int *varno)
{
if (IsA(node, RangeTblRef))
{
RangeTblRef *rangeTblRef = (RangeTblRef *) node;
return rangeTblRef->rtindex == *varno;
}
return expression_tree_walker(node, HasRangeTableRef, varno);
}
/* /*
* CreateRelationIsAKnownShardFilter constructs an expression of the form: * CreateRelationIsAKnownShardFilter constructs an expression of the form:
* pg_catalog.relation_is_a_known_shard(oid) IS NOT TRUE * pg_catalog.relation_is_a_known_shard(oid) IS NOT TRUE

View File

@ -83,13 +83,52 @@ SELECT relname FROM pg_catalog.pg_class WHERE relnamespace = 'mx_hide_shard_name
test_table test_table
(1 row) (1 row)
-- EVen when using subquery and having no existing quals on pg_clcass -- Even when using subquery and having no existing quals on pg_clcass
SELECT relname FROM (SELECT relname, relnamespace FROM pg_catalog.pg_class) AS q WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname; SELECT relname FROM (SELECT relname, relnamespace FROM pg_catalog.pg_class) AS q WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
relname relname
--------------------------------------------------------------------- ---------------------------------------------------------------------
test_table test_table
(1 row) (1 row)
-- Check that inserts into pg_class don't add the filter
EXPLAIN (COSTS OFF) INSERT INTO pg_class VALUES (1);
QUERY PLAN
---------------------------------------------------------------------
Insert on pg_class
-> Result
(2 rows)
-- Unless it's an INSERT SELECT that queries from pg_class;
EXPLAIN (COSTS OFF) INSERT INTO pg_class SELECT * FROM pg_class;
QUERY PLAN
---------------------------------------------------------------------
Insert on pg_class
-> Seq Scan on pg_class pg_class_1
Filter: (relation_is_a_known_shard(oid) IS NOT TRUE)
(3 rows)
-- Check that query that psql "\d test_table" does gets optimized to an index
-- scan
EXPLAIN (COSTS OFF) SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname OPERATOR(pg_catalog.~) '^(test_table)$' COLLATE pg_catalog.default
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;
QUERY PLAN
---------------------------------------------------------------------
Sort
Sort Key: n.nspname, c.relname
-> Nested Loop Left Join
Join Filter: (n.oid = c.relnamespace)
-> Index Scan using pg_class_relname_nsp_index on pg_class c
Index Cond: (relname = 'test_table'::text)
Filter: ((relname ~ '^(test_table)$'::text) AND (relation_is_a_known_shard(oid) IS NOT TRUE) AND pg_table_is_visible(oid))
-> Seq Scan on pg_namespace n
(8 rows)
commit prepared 'take-aggressive-lock'; commit prepared 'take-aggressive-lock';
-- now create an index -- now create an index
\c - - - :master_port \c - - - :master_port

View File

@ -50,9 +50,25 @@ prepare transaction 'take-aggressive-lock';
-- shards are hidden when using psql as application_name -- shards are hidden when using psql as application_name
SELECT relname FROM pg_catalog.pg_class WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname; SELECT relname FROM pg_catalog.pg_class WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
-- EVen when using subquery and having no existing quals on pg_clcass -- Even when using subquery and having no existing quals on pg_clcass
SELECT relname FROM (SELECT relname, relnamespace FROM pg_catalog.pg_class) AS q WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname; SELECT relname FROM (SELECT relname, relnamespace FROM pg_catalog.pg_class) AS q WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
-- Check that inserts into pg_class don't add the filter
EXPLAIN (COSTS OFF) INSERT INTO pg_class VALUES (1);
-- Unless it's an INSERT SELECT that queries from pg_class;
EXPLAIN (COSTS OFF) INSERT INTO pg_class SELECT * FROM pg_class;
-- Check that query that psql "\d test_table" does gets optimized to an index
-- scan
EXPLAIN (COSTS OFF) SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname OPERATOR(pg_catalog.~) '^(test_table)$' COLLATE pg_catalog.default
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;
commit prepared 'take-aggressive-lock'; commit prepared 'take-aggressive-lock';
-- now create an index -- now create an index