|
25 | 25 | #include "cdb/cdbplan.h" |
26 | 26 | #include "cdb/cdbvars.h" |
27 | 27 | #include "nodes/makefuncs.h" |
| 28 | +#include "nodes/nodeFuncs.h" |
28 | 29 | #include "optimizer/clauses.h" |
29 | 30 | #include "optimizer/optimizer.h" |
30 | 31 | #include "optimizer/orca.h" |
|
37 | 38 | #include "parser/parsetree.h" |
38 | 39 | #include "rewrite/rewriteManip.h" |
39 | 40 | #include "portability/instr_time.h" |
| 41 | +#include "utils/elog.h" |
40 | 42 | #include "utils/guc.h" |
41 | 43 | #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" |
42 | 48 |
|
43 | 49 | /* GPORCA entry point */ |
44 | 50 | extern PlannedStmt * GPOPTOptimizedPlan(Query *parse, bool *had_unexpected_failure, OptimizerOptions *opts); |
@@ -81,6 +87,110 @@ log_optimizer(PlannedStmt *plan, bool fUnexpectedFailure) |
81 | 87 | } |
82 | 88 | } |
83 | 89 |
|
| 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 | +} |
84 | 194 |
|
85 | 195 | /* |
86 | 196 | * optimize_query |
@@ -140,6 +250,23 @@ optimize_query(Query *parse, int cursorOptions, ParamListInfo boundParams, Optim |
140 | 250 | /* |
141 | 251 | * Pre-process the Query tree before calling optimizer. |
142 | 252 | * |
| 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 | + /* |
143 | 270 | * Constant folding will add dependencies to functions or relations in |
144 | 271 | * glob->invalItems, for any functions that are inlined or eliminated |
145 | 272 | * away. (We will find dependencies to other objects later, after planning). |
|
0 commit comments