Skip to content

Commit 0d40460

Browse files
committed
ORCA: prevent crashes with extension prosupport functions in NULL rtable queries
Add query_contains_support_functions() to detect extension prosupport functions before GPORCA planning and fall back to standard planner when rtable is NULL. The issue occurs during constant folding when extension prosupport functions (e.g., from pg_search) expect a valid rtable but encounter NULL rtable in sublink scenarios. This causes crashes in eval_const_expressions_mutator.
1 parent 40c096d commit 0d40460

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

  • src/backend/optimizer/plan

src/backend/optimizer/plan/orca.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "cdb/cdbplan.h"
2626
#include "cdb/cdbvars.h"
2727
#include "nodes/makefuncs.h"
28+
#include "nodes/nodeFuncs.h"
2829
#include "optimizer/clauses.h"
2930
#include "optimizer/optimizer.h"
3031
#include "optimizer/orca.h"
@@ -37,8 +38,13 @@
3738
#include "parser/parsetree.h"
3839
#include "rewrite/rewriteManip.h"
3940
#include "portability/instr_time.h"
41+
#include "utils/elog.h"
4042
#include "utils/guc.h"
4143
#include "utils/lsyscache.h"
44+
#include "utils/memutils.h"
45+
#include "utils/syscache.h"
46+
#include "catalog/pg_proc.h"
47+
#include "catalog/pg_namespace.h"
4248

4349
/* GPORCA entry point */
4450
extern PlannedStmt * GPOPTOptimizedPlan(Query *parse, bool *had_unexpected_failure, OptimizerOptions *opts);
@@ -81,6 +87,110 @@ log_optimizer(PlannedStmt *plan, bool fUnexpectedFailure)
8187
}
8288
}
8389

90+
/*
91+
* support_functions_check_context
92+
* Context structure for checking support functions, similar to eval_const_expressions_context
93+
*/
94+
typedef struct
95+
{
96+
PlannerInfo *root;
97+
bool recurse_queries; /* recurse into query structures */
98+
bool recurse_sublink_testexpr; /* recurse into sublink test expressions */
99+
} support_functions_check_context;
100+
101+
/*
102+
* check_support_functions_walker
103+
* Walker function to check if a query tree contains functions with support functions
104+
* Uses the same traversal pattern as eval_const_expressions_mutator but as a walker
105+
*/
106+
static bool
107+
check_support_functions_walker(Node *node, support_functions_check_context *context)
108+
{
109+
if (node == NULL)
110+
return false;
111+
112+
if (IsA(node, Query))
113+
{
114+
Query *query = (Query *) node;
115+
116+
if (context->recurse_queries)
117+
{
118+
/* Recurse into Query structures like fold_constants does */
119+
return query_tree_walker(query, check_support_functions_walker, context, 0);
120+
}
121+
return false;
122+
}
123+
124+
/* Handle SubLink like eval_const_expressions_mutator does */
125+
if (IsA(node, SubLink) && !context->recurse_sublink_testexpr)
126+
{
127+
SubLink *sublink = (SubLink *) node;
128+
129+
/*
130+
* Also invoke the walker on the sublink's Query node, so it
131+
* can recurse into the sub-query if it wants to.
132+
*/
133+
return query_tree_walker((Query *) sublink->subselect, check_support_functions_walker, context, 0);
134+
}
135+
136+
/* Check both FuncExpr and OpExpr nodes for prosupport functions */
137+
if (IsA(node, FuncExpr) || IsA(node, OpExpr))
138+
{
139+
HeapTuple proctup;
140+
Form_pg_proc procform;
141+
bool has_support = false;
142+
Oid funcid;
143+
const char *exprtype;
144+
145+
/* Extract function OID from either FuncExpr or OpExpr */
146+
if (IsA(node, FuncExpr))
147+
{
148+
FuncExpr *funcexpr = (FuncExpr *) node;
149+
funcid = funcexpr->funcid;
150+
exprtype = "FuncExpr";
151+
}
152+
else /* OpExpr */
153+
{
154+
OpExpr *opexpr = (OpExpr *) node;
155+
funcid = opexpr->opfuncid;
156+
exprtype = "OpExpr";
157+
}
158+
159+
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
160+
if (HeapTupleIsValid(proctup))
161+
{
162+
procform = (Form_pg_proc) GETSTRUCT(proctup);
163+
has_support = OidIsValid(procform->prosupport);
164+
/* Skip pg_catalog namespace support functions - they are safe */
165+
if (has_support && procform->pronamespace != PG_CATALOG_NAMESPACE)
166+
{
167+
elog(DEBUG1, "Found %s with non-pg_catalog prosupport function, falling back to standard planner", exprtype);
168+
ReleaseSysCache(proctup);
169+
return true;
170+
}
171+
ReleaseSysCache(proctup);
172+
}
173+
}
174+
175+
return expression_tree_walker(node, check_support_functions_walker, context);
176+
}
177+
178+
/*
179+
* query_contains_support_functions
180+
* Check if a query contains function calls that have support functions
181+
*/
182+
static bool
183+
query_contains_support_functions(Query *query)
184+
{
185+
support_functions_check_context context;
186+
187+
context.root = NULL; /* We don't need PlannerInfo for this check */
188+
context.recurse_queries = true; /* recurse into query structures */
189+
context.recurse_sublink_testexpr = false; /* do not recurse into sublink test expressions */
190+
191+
/* Use the same traversal pattern as fold_constants but with a walker */
192+
return query_or_expression_tree_walker((Node *) query, check_support_functions_walker, &context, 0);
193+
}
84194

85195
/*
86196
* optimize_query
@@ -140,6 +250,23 @@ optimize_query(Query *parse, int cursorOptions, ParamListInfo boundParams, Optim
140250
/*
141251
* Pre-process the Query tree before calling optimizer.
142252
*
253+
* Check if rtable is NULL and query contains support functions.
254+
* If both conditions are true, fall back to standard planner to avoid
255+
* crashes in extension support functions that expect valid rtable.
256+
*
257+
* Performance note: This check does not significantly impact performance
258+
* since pqueryCopy->rtable is non-NULL for most queries. The rtable is
259+
* typically only NULL when the query contains sublinks, which is uncommon.
260+
* The query_contains_support_functions() traversal only occurs in these
261+
* rare cases.
262+
*/
263+
if (pqueryCopy->rtable == NULL && query_contains_support_functions(pqueryCopy))
264+
{
265+
elog(DEBUG1, "Query rtable is NULL and contains support functions, falling back to standard planner");
266+
return NULL;
267+
}
268+
269+
/*
143270
* Constant folding will add dependencies to functions or relations in
144271
* glob->invalItems, for any functions that are inlined or eliminated
145272
* away. (We will find dependencies to other objects later, after planning).

0 commit comments

Comments
 (0)