diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2022-01-09 12:43:09 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2022-01-09 12:43:09 -0500 |
commit | 6867f963e319934cbdafeb4bd5beaea5797c7be2 (patch) | |
tree | c055dadf62dfd6b32ad9b631dcd3065e30acd8ce /src/backend/utils/adt/ruleutils.c | |
parent | 96a6f11c06252b046ca9a6ee1f581a2f5d599301 (diff) | |
download | postgresql-6867f963e319934cbdafeb4bd5beaea5797c7be2.tar.gz postgresql-6867f963e319934cbdafeb4bd5beaea5797c7be2.zip |
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
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 44 |
1 files changed, 43 insertions, 1 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 039f897331c..63b85566822 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2561,6 +2561,12 @@ decompile_column_index_array(Datum column_index_array, Oid relId, * the one specified by the second parameter. This is sufficient for * partial indexes, column default expressions, etc. We also support * Var-free expressions, for which the OID can be InvalidOid. + * + * We expect this function to work, or throw a reasonably clean error, + * for any node tree that can appear in a catalog pg_node_tree column. + * Query trees, such as those appearing in pg_rewrite.ev_action, are + * not supported. Nor are expressions in more than one relation, which + * can appear in places like pg_rewrite.ev_qual. * ---------- */ Datum @@ -2622,11 +2628,13 @@ static text * pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags) { Node *node; + Node *tst; + Relids relids; List *context; char *exprstr; char *str; - /* Convert input TEXT object to C string */ + /* Convert input pg_node_tree (really TEXT) object to C string */ exprstr = text_to_cstring(expr); /* Convert expression to node tree */ @@ -2634,6 +2642,40 @@ pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags) pfree(exprstr); + /* + * Throw error if the input is a querytree rather than an expression tree. + * While we could support queries here, there seems no very good reason + * to. In most such catalog columns, we'll see a List of Query nodes, or + * even nested Lists, so drill down to a non-List node before checking. + */ + tst = node; + while (tst && IsA(tst, List)) + tst = linitial((List *) tst); + if (tst && IsA(tst, Query)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input is a query, not an expression"))); + + /* + * Throw error if the expression contains Vars we won't be able to + * deparse. + */ + relids = pull_varnos(NULL, node); + if (OidIsValid(relid)) + { + if (!bms_is_subset(relids, bms_make_singleton(1))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("expression contains variables of more than one relation"))); + } + else + { + if (!bms_is_empty(relids)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("expression contains variables"))); + } + /* Prepare deparse context if needed */ if (OidIsValid(relid)) context = deparse_context_for(relname, relid); |