aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/heap.c7
-rw-r--r--src/backend/commands/copyfrom.c6
-rw-r--r--src/backend/commands/indexcmds.c31
-rw-r--r--src/backend/commands/tablecmds.c50
-rw-r--r--src/backend/optimizer/util/clauses.c66
5 files changed, 104 insertions, 56 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3fd44eeb548..bcf7d4c28de 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2658,7 +2658,8 @@ AddRelationNewConstraints(Relation rel,
continue;
/* If the DEFAULT is volatile we cannot use a missing value */
- if (colDef->missingMode && contain_volatile_functions((Node *) expr))
+ if (colDef->missingMode &&
+ contain_volatile_functions_after_planning((Expr *) expr))
colDef->missingMode = false;
defOid = StoreAttrDefault(rel, colDef->attnum, expr, is_internal,
@@ -3093,9 +3094,11 @@ cookDefault(ParseState *pstate,
if (attgenerated)
{
+ /* Disallow refs to other generated columns */
check_nested_generated(pstate, expr);
- if (contain_mutable_functions(expr))
+ /* Disallow mutable functions */
+ if (contain_mutable_functions_after_planning((Expr *) expr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("generation expression is not immutable")));
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index 63c4da07814..91da191836d 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -755,6 +755,9 @@ CopyFrom(CopyFromState cstate)
* Can't support multi-inserts if there are any volatile function
* expressions in WHERE clause. Similarly to the trigger case above,
* such expressions may query the table we're inserting into.
+ *
+ * Note: the whereClause was already preprocessed in DoCopy(), so it's
+ * okay to use contain_volatile_functions() directly.
*/
insertMethod = CIM_SINGLE;
}
@@ -1454,7 +1457,8 @@ BeginCopyFrom(ParseState *pstate,
* known to be safe for use with the multi-insert
* optimization. Hence we use this special case function
* checker rather than the standard check for
- * contain_volatile_functions().
+ * contain_volatile_functions(). Note also that we already
+ * ran the expression through expression_planner().
*/
if (!volatile_defexprs)
volatile_defexprs = contain_volatile_functions_not_nextval((Node *) defexpr);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index cd25ac2e4df..920f3baadf6 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1721,33 +1721,6 @@ DefineIndex(Oid relationId,
/*
- * CheckMutability
- * Test whether given expression is mutable
- */
-static bool
-CheckMutability(Expr *expr)
-{
- /*
- * First run the expression through the planner. This has a couple of
- * important consequences. First, function default arguments will get
- * inserted, which may affect volatility (consider "default now()").
- * Second, inline-able functions will get inlined, which may allow us to
- * conclude that the function is really less volatile than it's marked. As
- * an example, polymorphic functions must be marked with the most volatile
- * behavior that they have for any input type, but once we inline the
- * function we may be able to conclude that it's not so volatile for the
- * particular input type we're dealing with.
- *
- * We assume here that expression_planner() won't scribble on its input.
- */
- expr = expression_planner(expr);
-
- /* Now we can search for non-immutable functions */
- return contain_mutable_functions((Node *) expr);
-}
-
-
-/*
* CheckPredicate
* Checks that the given partial-index predicate is valid.
*
@@ -1770,7 +1743,7 @@ CheckPredicate(Expr *predicate)
* A predicate using mutable functions is probably wrong, for the same
* reasons that we don't allow an index expression to use one.
*/
- if (CheckMutability(predicate))
+ if (contain_mutable_functions_after_planning(predicate))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index predicate must be marked IMMUTABLE")));
@@ -1913,7 +1886,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
* same data every time, it's not clear what the index entries
* mean at all.
*/
- if (CheckMutability((Expr *) expr))
+ if (contain_mutable_functions_after_planning((Expr *) expr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index expression must be marked IMMUTABLE")));
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7afb4b77b46..616f23a58c4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -16861,30 +16861,6 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
*partexprs = lappend(*partexprs, expr);
/*
- * Try to simplify the expression before checking for
- * mutability. The main practical value of doing it in this
- * order is that an inline-able SQL-language function will be
- * accepted if its expansion is immutable, whether or not the
- * function itself is marked immutable.
- *
- * Note that expression_planner does not change the passed in
- * expression destructively and we have already saved the
- * expression to be stored into the catalog above.
- */
- expr = (Node *) expression_planner((Expr *) expr);
-
- /*
- * Partition expression cannot contain mutable functions,
- * because a given row must always map to the same partition
- * as long as there is no change in the partition boundary
- * structure.
- */
- if (contain_mutable_functions(expr))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("functions in partition key expression must be marked IMMUTABLE")));
-
- /*
* transformPartitionSpec() should have already rejected
* subqueries, aggregates, window functions, and SRFs, based
* on the EXPR_KIND_ for partition expressions.
@@ -16926,6 +16902,32 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
}
/*
+ * Preprocess the expression before checking for mutability.
+ * This is essential for the reasons described in
+ * contain_mutable_functions_after_planning. However, we call
+ * expression_planner for ourselves rather than using that
+ * function, because if constant-folding reduces the
+ * expression to a constant, we'd like to know that so we can
+ * complain below.
+ *
+ * Like contain_mutable_functions_after_planning, assume that
+ * expression_planner won't scribble on its input, so this
+ * won't affect the partexprs entry we saved above.
+ */
+ expr = (Node *) expression_planner((Expr *) expr);
+
+ /*
+ * Partition expressions cannot contain mutable functions,
+ * because a given row must always map to the same partition
+ * as long as there is no change in the partition boundary
+ * structure.
+ */
+ if (contain_mutable_functions(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("functions in partition key expression must be marked IMMUTABLE")));
+
+ /*
* While it is not exactly *wrong* for a partition expression
* to be a constant, it seems better to reject such keys.
*/
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index da50bef7a07..cd3004e57ed 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -357,6 +357,11 @@ contain_subplans_walker(Node *node, void *context)
* mistakenly think that something like "WHERE random() < 0.5" can be treated
* as a constant qualification.
*
+ * This will give the right answer only for clauses that have been put
+ * through expression preprocessing. Callers outside the planner typically
+ * should use contain_mutable_functions_after_planning() instead, for the
+ * reasons given there.
+ *
* We will recursively look into Query nodes (i.e., SubLink sub-selects)
* but not into SubPlans. See comments for contain_volatile_functions().
*/
@@ -416,6 +421,34 @@ contain_mutable_functions_walker(Node *node, void *context)
context);
}
+/*
+ * contain_mutable_functions_after_planning
+ * Test whether given expression contains mutable functions.
+ *
+ * This is a wrapper for contain_mutable_functions() that is safe to use from
+ * outside the planner. The difference is that it first runs the expression
+ * through expression_planner(). There are two key reasons why we need that:
+ *
+ * First, function default arguments will get inserted, which may affect
+ * volatility (consider "default now()").
+ *
+ * Second, inline-able functions will get inlined, which may allow us to
+ * conclude that the function is really less volatile than it's marked.
+ * As an example, polymorphic functions must be marked with the most volatile
+ * behavior that they have for any input type, but once we inline the
+ * function we may be able to conclude that it's not so volatile for the
+ * particular input type we're dealing with.
+ */
+bool
+contain_mutable_functions_after_planning(Expr *expr)
+{
+ /* We assume here that expression_planner() won't scribble on its input */
+ expr = expression_planner(expr);
+
+ /* Now we can search for non-immutable functions */
+ return contain_mutable_functions((Node *) expr);
+}
+
/*****************************************************************************
* Check clauses for volatile functions
@@ -429,6 +462,11 @@ contain_mutable_functions_walker(Node *node, void *context)
* volatile function) is found. This test prevents, for example,
* invalid conversions of volatile expressions into indexscan quals.
*
+ * This will give the right answer only for clauses that have been put
+ * through expression preprocessing. Callers outside the planner typically
+ * should use contain_volatile_functions_after_planning() instead, for the
+ * reasons given there.
+ *
* We will recursively look into Query nodes (i.e., SubLink sub-selects)
* but not into SubPlans. This is a bit odd, but intentional. If we are
* looking at a SubLink, we are probably deciding whether a query tree
@@ -553,6 +591,34 @@ contain_volatile_functions_walker(Node *node, void *context)
}
/*
+ * contain_volatile_functions_after_planning
+ * Test whether given expression contains volatile functions.
+ *
+ * This is a wrapper for contain_volatile_functions() that is safe to use from
+ * outside the planner. The difference is that it first runs the expression
+ * through expression_planner(). There are two key reasons why we need that:
+ *
+ * First, function default arguments will get inserted, which may affect
+ * volatility (consider "default random()").
+ *
+ * Second, inline-able functions will get inlined, which may allow us to
+ * conclude that the function is really less volatile than it's marked.
+ * As an example, polymorphic functions must be marked with the most volatile
+ * behavior that they have for any input type, but once we inline the
+ * function we may be able to conclude that it's not so volatile for the
+ * particular input type we're dealing with.
+ */
+bool
+contain_volatile_functions_after_planning(Expr *expr)
+{
+ /* We assume here that expression_planner() won't scribble on its input */
+ expr = expression_planner(expr);
+
+ /* Now we can search for volatile functions */
+ return contain_volatile_functions((Node *) expr);
+}
+
+/*
* Special purpose version of contain_volatile_functions() for use in COPY:
* ignore nextval(), but treat all other functions normally.
*/