Skip to content

Commit 6867f96

Browse files
committed
Make pg_get_expr() more bulletproof.
Since this function is defined to accept pg_node_tree values, it could get applied to any nodetree that can appear in a cataloged pg_node_tree column. Some such cases can't be supported --- for example, its API doesn't allow providing referents for more than one relation --- but we should try to throw a user-facing error rather than an internal error when encountering such a case. In support of this, extend expression_tree_walker/mutator to be sure they'll work on any such node tree (which basically means adding support for relpartbound node types). That allows us to run pull_varnos and check for the case of multiple relations before we start processing the tree. The alternative of changing the low-level error thrown for an out-of-range varno isn't appealing, because that could mask actual bugs in other usages of ruleutils. Per report from Justin Pryzby. This is basically cosmetic, so no back-patch. Discussion: https://postgr.es/m/20211219205422.GT17618@telsasoft.com
1 parent 96a6f11 commit 6867f96

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

src/backend/nodes/nodeFuncs.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,6 +2201,26 @@ expression_tree_walker(Node *node,
22012201
return true;
22022202
}
22032203
break;
2204+
case T_PartitionBoundSpec:
2205+
{
2206+
PartitionBoundSpec *pbs = (PartitionBoundSpec *) node;
2207+
2208+
if (walker(pbs->listdatums, context))
2209+
return true;
2210+
if (walker(pbs->lowerdatums, context))
2211+
return true;
2212+
if (walker(pbs->upperdatums, context))
2213+
return true;
2214+
}
2215+
break;
2216+
case T_PartitionRangeDatum:
2217+
{
2218+
PartitionRangeDatum *prd = (PartitionRangeDatum *) node;
2219+
2220+
if (walker(prd->value, context))
2221+
return true;
2222+
}
2223+
break;
22042224
case T_List:
22052225
foreach(temp, (List *) node)
22062226
{
@@ -3092,6 +3112,28 @@ expression_tree_mutator(Node *node,
30923112
return (Node *) newnode;
30933113
}
30943114
break;
3115+
case T_PartitionBoundSpec:
3116+
{
3117+
PartitionBoundSpec *pbs = (PartitionBoundSpec *) node;
3118+
PartitionBoundSpec *newnode;
3119+
3120+
FLATCOPY(newnode, pbs, PartitionBoundSpec);
3121+
MUTATE(newnode->listdatums, pbs->listdatums, List *);
3122+
MUTATE(newnode->lowerdatums, pbs->lowerdatums, List *);
3123+
MUTATE(newnode->upperdatums, pbs->upperdatums, List *);
3124+
return (Node *) newnode;
3125+
}
3126+
break;
3127+
case T_PartitionRangeDatum:
3128+
{
3129+
PartitionRangeDatum *prd = (PartitionRangeDatum *) node;
3130+
PartitionRangeDatum *newnode;
3131+
3132+
FLATCOPY(newnode, prd, PartitionRangeDatum);
3133+
MUTATE(newnode->value, prd->value, Node *);
3134+
return (Node *) newnode;
3135+
}
3136+
break;
30953137
case T_List:
30963138
{
30973139
/*

src/backend/optimizer/util/var.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ static Relids alias_relid_set(Query *query, Relids relids);
8888
* Create a set of all the distinct varnos present in a parsetree.
8989
* Only varnos that reference level-zero rtable entries are considered.
9090
*
91+
* "root" can be passed as NULL if it is not necessary to process
92+
* PlaceHolderVars.
93+
*
9194
* NOTE: this is used on not-yet-planned expressions. It may therefore find
9295
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
9396
* references to the desired rtable level! But when we find a completed
@@ -168,9 +171,13 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
168171
/*
169172
* If a PlaceHolderVar is not of the target query level, ignore it,
170173
* instead recursing into its expression to see if it contains any
171-
* vars that are of the target level.
174+
* vars that are of the target level. We'll also do that when the
175+
* caller doesn't pass a "root" pointer. (We probably shouldn't see
176+
* PlaceHolderVars at all in such cases, but if we do, this is a
177+
* reasonable behavior.)
172178
*/
173-
if (phv->phlevelsup == context->sublevels_up)
179+
if (phv->phlevelsup == context->sublevels_up &&
180+
context->root != NULL)
174181
{
175182
/*
176183
* Ideally, the PHV's contribution to context->varnos is its

src/backend/utils/adt/ruleutils.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2561,6 +2561,12 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
25612561
* the one specified by the second parameter. This is sufficient for
25622562
* partial indexes, column default expressions, etc. We also support
25632563
* Var-free expressions, for which the OID can be InvalidOid.
2564+
*
2565+
* We expect this function to work, or throw a reasonably clean error,
2566+
* for any node tree that can appear in a catalog pg_node_tree column.
2567+
* Query trees, such as those appearing in pg_rewrite.ev_action, are
2568+
* not supported. Nor are expressions in more than one relation, which
2569+
* can appear in places like pg_rewrite.ev_qual.
25642570
* ----------
25652571
*/
25662572
Datum
@@ -2622,18 +2628,54 @@ static text *
26222628
pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags)
26232629
{
26242630
Node *node;
2631+
Node *tst;
2632+
Relids relids;
26252633
List *context;
26262634
char *exprstr;
26272635
char *str;
26282636

2629-
/* Convert input TEXT object to C string */
2637+
/* Convert input pg_node_tree (really TEXT) object to C string */
26302638
exprstr = text_to_cstring(expr);
26312639

26322640
/* Convert expression to node tree */
26332641
node = (Node *) stringToNode(exprstr);
26342642

26352643
pfree(exprstr);
26362644

2645+
/*
2646+
* Throw error if the input is a querytree rather than an expression tree.
2647+
* While we could support queries here, there seems no very good reason
2648+
* to. In most such catalog columns, we'll see a List of Query nodes, or
2649+
* even nested Lists, so drill down to a non-List node before checking.
2650+
*/
2651+
tst = node;
2652+
while (tst && IsA(tst, List))
2653+
tst = linitial((List *) tst);
2654+
if (tst && IsA(tst, Query))
2655+
ereport(ERROR,
2656+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2657+
errmsg("input is a query, not an expression")));
2658+
2659+
/*
2660+
* Throw error if the expression contains Vars we won't be able to
2661+
* deparse.
2662+
*/
2663+
relids = pull_varnos(NULL, node);
2664+
if (OidIsValid(relid))
2665+
{
2666+
if (!bms_is_subset(relids, bms_make_singleton(1)))
2667+
ereport(ERROR,
2668+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2669+
errmsg("expression contains variables of more than one relation")));
2670+
}
2671+
else
2672+
{
2673+
if (!bms_is_empty(relids))
2674+
ereport(ERROR,
2675+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2676+
errmsg("expression contains variables")));
2677+
}
2678+
26372679
/* Prepare deparse context if needed */
26382680
if (OidIsValid(relid))
26392681
context = deparse_context_for(relname, relid);

0 commit comments

Comments
 (0)