aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-01-09 12:43:09 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2022-01-09 12:43:09 -0500
commit6867f963e319934cbdafeb4bd5beaea5797c7be2 (patch)
treec055dadf62dfd6b32ad9b631dcd3065e30acd8ce /src/backend/utils/adt/ruleutils.c
parent96a6f11c06252b046ca9a6ee1f581a2f5d599301 (diff)
downloadpostgresql-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.c44
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);