Skip to content

Commit 9401cf3

Browse files
committed
Support matview status maintenance for multi-table JOIN queries
Previously, GetViewBaseRelids() rejected any query with more than one base table, so materialized views defined with JOINs were never registered in gp_matview_aux/gp_matview_tables. This meant no status tracking and no staleness propagation for join matviews. Add a recursive helper extract_base_relids_from_jointree() that walks RangeTblRef, JoinExpr, and FromExpr nodes to collect all base relation OIDs. This is the only C function changed -- the existing downstream infrastructure (InsertMatviewTablesEntries, SetRelativeMatviewAuxStatus, MaintainMaterializedViewStatus, reference counting) already supports N base tables per matview. This is a first step toward AQUMV support for join queries. Users can also inspect a join matview's freshness status manually via gp_matview_aux. Key behaviors: - Self-joins (t1 JOIN t1) are deduplicated to one catalog entry - All join types supported: INNER, LEFT, RIGHT, FULL, implicit cross - Subquery/function RTEs in FROM are still rejected - Partitioned tables in joins propagate DML status correctly - Status escalation across multiple base tables works (i→e on delete) - Transaction rollback correctly reverts status changes Includes regression tests for: two/three-table joins, implicit joins, self-joins, all outer join types, mixed join types, join with GROUP BY, shared base tables across multiple MVs, multi-DML transactions, transaction rollback, cross joins, partitioned tables in joins, VACUUM FULL, TRUNCATE, WITH NO DATA, and DROP CASCADE.
1 parent 7e867f6 commit 9401cf3

3 files changed

Lines changed: 834 additions & 79 deletions

File tree

src/backend/catalog/gp_matview_aux.c

Lines changed: 98 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
#include "optimizer/transform.h"
5353
#include "parser/parsetree.h"
5454

55+
static bool extract_base_relids_from_jointree(Node *jtnode, List *rtable,
56+
List **relids, bool *has_foreign);
57+
5558
static void InsertMatviewTablesEntries(Oid mvoid, List *relids);
5659

5760
static void RemoveMatviewTablesEntries(Oid mvoid);
@@ -60,20 +63,106 @@ static void SetMatviewAuxStatus_guts(Oid mvoid, char status);
6063

6164
static void addRelationMVRefCount(Oid relid, int32 mvrefcount);
6265

66+
/*
67+
* extract_base_relids_from_jointree
68+
* Recursively walk a join tree node and collect base relation OIDs.
69+
*
70+
* Handles RangeTblRef (leaf), JoinExpr (explicit JOIN), and FromExpr
71+
* (implicit cross-join / comma-separated FROM list).
72+
*
73+
* Returns false if any unsupported RTE kind is found (subquery, function,
74+
* CTE, etc.). Self-joins are deduplicated via list_append_unique_oid.
75+
*/
76+
static bool
77+
extract_base_relids_from_jointree(Node *jtnode, List *rtable,
78+
List **relids, bool *has_foreign)
79+
{
80+
if (jtnode == NULL)
81+
return false;
82+
83+
if (IsA(jtnode, RangeTblRef))
84+
{
85+
int rtindex = ((RangeTblRef *) jtnode)->rtindex;
86+
RangeTblEntry *rte = rt_fetch(rtindex, rtable);
87+
char relkind;
88+
bool can_be_partition;
89+
90+
if (rte->rtekind != RTE_RELATION)
91+
return false;
92+
93+
relkind = get_rel_relkind(rte->relid);
94+
95+
/*
96+
* Allow foreign table here, however we don't know if the data is
97+
* up to date or not of the view.
98+
* But if users want to query matview instead of query foreign tables
99+
* outside CBDB, let them decide with aqumv_allow_foreign_table.
100+
*/
101+
if (relkind != RELKIND_RELATION &&
102+
relkind != RELKIND_PARTITIONED_TABLE &&
103+
relkind != RELKIND_FOREIGN_TABLE)
104+
return false;
105+
106+
if (has_foreign && relkind == RELKIND_FOREIGN_TABLE)
107+
*has_foreign = true;
108+
109+
/*
110+
* Inherit tables are not supported.
111+
*/
112+
can_be_partition = (relkind == RELKIND_PARTITIONED_TABLE) ||
113+
get_rel_relispartition(rte->relid);
114+
115+
if (!can_be_partition &&
116+
(has_superclass(rte->relid) || has_subclass(rte->relid)))
117+
return false;
118+
119+
/* Deduplicate for self-joins (t1 JOIN t1). */
120+
*relids = list_append_unique_oid(*relids, rte->relid);
121+
122+
return true;
123+
}
124+
else if (IsA(jtnode, JoinExpr))
125+
{
126+
JoinExpr *j = (JoinExpr *) jtnode;
127+
128+
if (!extract_base_relids_from_jointree(j->larg, rtable, relids, has_foreign))
129+
return false;
130+
if (!extract_base_relids_from_jointree(j->rarg, rtable, relids, has_foreign))
131+
return false;
132+
133+
return true;
134+
}
135+
else if (IsA(jtnode, FromExpr))
136+
{
137+
FromExpr *f = (FromExpr *) jtnode;
138+
ListCell *lc;
139+
140+
foreach(lc, f->fromlist)
141+
{
142+
if (!extract_base_relids_from_jointree((Node *) lfirst(lc),
143+
rtable, relids, has_foreign))
144+
return false;
145+
}
146+
147+
return true;
148+
}
149+
150+
/* Unsupported node type */
151+
return false;
152+
}
153+
63154
/*
64155
* GetViewBaseRelids
65156
* Get all base tables's oid of a query tree.
66-
* Currently there is only one base table, but there should be
67-
* distinct func on it later. Self join tables: t1 join t1, will
68-
* get only one oid.
69-
*
157+
* Supports single-table and multi-table (JOIN) queries.
158+
* Self join tables: t1 join t1, will get only one oid.
159+
*
70160
* Return NIL if the query we think it's useless.
71161
*/
72162
List*
73163
GetViewBaseRelids(const Query *viewQuery, bool *has_foreign)
74164
{
75165
List *relids = NIL;
76-
Node *mvjtnode;
77166

78167
if ((viewQuery->commandType != CMD_SELECT) ||
79168
(viewQuery->rowMarks != NIL) ||
@@ -109,44 +198,14 @@ GetViewBaseRelids(const Query *viewQuery, bool *has_foreign)
109198
if (contain_mutable_functions((Node*)viewQuery))
110199
return NIL;
111200

112-
if (list_length(viewQuery->jointree->fromlist) != 1)
113-
return NIL;
114-
115-
mvjtnode = (Node *) linitial(viewQuery->jointree->fromlist);
116-
if (!IsA(mvjtnode, RangeTblRef))
117-
return NIL;
118-
119-
RangeTblEntry *rte = rt_fetch(1, viewQuery->rtable);
120-
if (rte->rtekind != RTE_RELATION)
121-
return NIL;
122-
123-
char relkind = get_rel_relkind(rte->relid);
124-
125-
/*
126-
* Allow foreign table here, however we don't know if the data is
127-
* up to date or not of the view.
128-
* But if users want to query matview instead of query foreign tables
129-
* outside CBDB, let them decide with aqumv_allow_foreign_table.
130-
*/
131-
if (relkind != RELKIND_RELATION &&
132-
relkind != RELKIND_PARTITIONED_TABLE &&
133-
relkind != RELKIND_FOREIGN_TABLE)
134-
return NIL;
135-
136201
if (has_foreign)
137-
*has_foreign = relkind == RELKIND_FOREIGN_TABLE;
202+
*has_foreign = false;
138203

139-
/*
140-
* inherit tables are not supported.
141-
*/
142-
bool can_be_partition = (relkind == RELKIND_PARTITIONED_TABLE) || get_rel_relispartition(rte->relid);
143-
144-
if (!can_be_partition &&
145-
(has_superclass(rte->relid) || has_subclass(rte->relid)))
204+
if (!extract_base_relids_from_jointree((Node *) viewQuery->jointree,
205+
viewQuery->rtable,
206+
&relids, has_foreign))
146207
return NIL;
147208

148-
relids = list_make1_oid(rte->relid);
149-
150209
return relids;
151210
}
152211

0 commit comments

Comments
 (0)