diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/heap.c | 7 | ||||
-rw-r--r-- | src/backend/commands/copyfrom.c | 6 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 31 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 50 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 66 |
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. */ |