aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/clauses.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r--src/backend/optimizer/util/clauses.c571
1 files changed, 7 insertions, 564 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index c6be4f87c2d..f3786dd2b63 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -19,18 +19,13 @@
#include "postgres.h"
-#include "access/genam.h"
#include "access/htup_details.h"
-#include "access/table.h"
-#include "access/xact.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_class.h"
-#include "catalog/pg_constraint.h"
#include "catalog/pg_language.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/functions.h"
#include "funcapi.h"
@@ -48,8 +43,6 @@
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
-#include "parser/parsetree.h"
-#include "partitioning/partdesc.h"
#include "rewrite/rewriteManip.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@@ -58,8 +51,6 @@
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
-#include "utils/partcache.h"
-#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -97,9 +88,6 @@ typedef struct
char max_hazard; /* worst proparallel hazard found so far */
char max_interesting; /* worst proparallel hazard of interest */
List *safe_param_ids; /* PARAM_EXEC Param IDs to treat as safe */
- RangeTblEntry *target_rte; /* query's target relation if any */
- CmdType command_type; /* query's command type */
- PlannerGlobal *planner_global; /* global info for planner invocation */
} max_parallel_hazard_context;
static bool contain_agg_clause_walker(Node *node, void *context);
@@ -110,20 +98,6 @@ static bool contain_volatile_functions_walker(Node *node, void *context);
static bool contain_volatile_functions_not_nextval_walker(Node *node, void *context);
static bool max_parallel_hazard_walker(Node *node,
max_parallel_hazard_context *context);
-static bool target_rel_max_parallel_hazard(max_parallel_hazard_context *context);
-static bool target_rel_max_parallel_hazard_recurse(Relation relation,
- CmdType command_type,
- max_parallel_hazard_context *context);
-static bool target_rel_trigger_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context);
-static bool target_rel_index_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context);
-static bool target_rel_domain_max_parallel_hazard(Oid typid,
- max_parallel_hazard_context *context);
-static bool target_rel_partitions_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context);
-static bool target_rel_chk_constr_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context);
static bool contain_nonstrict_functions_walker(Node *node, void *context);
static bool contain_exec_param_walker(Node *node, List *param_ids);
static bool contain_context_dependent_node(Node *clause);
@@ -571,19 +545,14 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context)
* later, in the common case where everything is SAFE.
*/
char
-max_parallel_hazard(Query *parse, PlannerGlobal *glob)
+max_parallel_hazard(Query *parse)
{
max_parallel_hazard_context context;
context.max_hazard = PROPARALLEL_SAFE;
context.max_interesting = PROPARALLEL_UNSAFE;
context.safe_param_ids = NIL;
- context.target_rte = parse->resultRelation > 0 ?
- rt_fetch(parse->resultRelation, parse->rtable) : NULL;
- context.command_type = parse->commandType;
- context.planner_global = glob;
(void) max_parallel_hazard_walker((Node *) parse, &context);
-
return context.max_hazard;
}
@@ -614,10 +583,6 @@ is_parallel_safe(PlannerInfo *root, Node *node)
context.max_hazard = PROPARALLEL_SAFE;
context.max_interesting = PROPARALLEL_RESTRICTED;
context.safe_param_ids = NIL;
- /* We don't need to evaluate target relation's parallel-safety here. */
- context.target_rte = NULL;
- context.command_type = CMD_UNKNOWN;
- context.planner_global = NULL;
/*
* The params that refer to the same or parent query level are considered
@@ -690,20 +655,14 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
* opclass support functions are generally parallel-safe. XmlExpr is a
* bit more dubious but we can probably get away with it. We err on the
* side of caution by treating CoerceToDomain as parallel-restricted.
- * However, for table modification statements, we check the parallel
- * safety of domain constraints as that could contain a parallel-unsafe
- * function, and executing that in parallel mode will lead to error.
- * SQLValueFunction should be safe in all cases. NextValueExpr is
- * parallel-unsafe.
+ * (Note: in principle that's wrong because a domain constraint could
+ * contain a parallel-unsafe function; but useful constraints probably
+ * never would have such, and assuming they do would cripple use of
+ * parallel query in the presence of domain types.) SQLValueFunction
+ * should be safe in all cases. NextValueExpr is parallel-unsafe.
*/
if (IsA(node, CoerceToDomain))
{
- if (context->target_rte != NULL)
- {
- if (target_rel_domain_max_parallel_hazard(((CoerceToDomain *) node)->resulttype, context))
- return true;
- }
-
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
return true;
}
@@ -729,27 +688,6 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
}
/*
- * ModifyingCTE expressions are treated as parallel-unsafe.
- *
- * XXX Normally, if the Query has a modifying CTE, the hasModifyingCTE
- * flag is set in the Query tree, and the query will be regarded as
- * parallel-usafe. However, in some cases, a re-written query with a
- * modifying CTE does not have that flag set, due to a bug in the query
- * rewriter.
- */
- else if (IsA(node, CommonTableExpr))
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
- Query *ctequery = castNode(Query, cte->ctequery);
-
- if (ctequery->commandType != CMD_SELECT)
- {
- context->max_hazard = PROPARALLEL_UNSAFE;
- return true;
- }
- }
-
- /*
* As a notational convenience for callers, look through RestrictInfo.
*/
else if (IsA(node, RestrictInfo))
@@ -819,19 +757,6 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
}
return false; /* nothing to recurse to */
}
- else if (IsA(node, RangeTblEntry))
- {
- RangeTblEntry *rte = (RangeTblEntry *) node;
-
- /* Nothing interesting to check for SELECTs */
- if (context->target_rte == NULL)
- return false;
-
- if (rte == context->target_rte)
- return target_rel_max_parallel_hazard(context);
-
- return false;
- }
/*
* When we're first invoked on a completely unplanned tree, we must
@@ -852,9 +777,7 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
/* Recurse into subselects */
return query_tree_walker(query,
max_parallel_hazard_walker,
- context,
- context->target_rte != NULL ?
- QTW_EXAMINE_RTES_BEFORE : 0);
+ context, 0);
}
/* Recurse to check arguments */
@@ -863,486 +786,6 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
context);
}
-/*
- * target_rel_max_parallel_hazard
- *
- * Determines the maximum parallel-mode hazard level for modification
- * of a specified relation.
- */
-static bool
-target_rel_max_parallel_hazard(max_parallel_hazard_context *context)
-{
- bool max_hazard_found;
-
- Relation targetRel;
-
- /*
- * The target table is already locked by the caller (this is done in the
- * parse/analyze phase), and remains locked until end-of-transaction.
- */
- targetRel = table_open(context->target_rte->relid, NoLock);
- max_hazard_found = target_rel_max_parallel_hazard_recurse(targetRel,
- context->command_type,
- context);
- table_close(targetRel, NoLock);
-
- return max_hazard_found;
-}
-
-static bool
-target_rel_max_parallel_hazard_recurse(Relation rel,
- CmdType command_type,
- max_parallel_hazard_context *context)
-{
- /* Currently only CMD_INSERT is supported */
- Assert(command_type == CMD_INSERT);
-
- /*
- * We can't support table modification in a parallel worker if it's a
- * foreign table/partition (no FDW API for supporting parallel access) or
- * a temporary table.
- */
- if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
- RelationUsesLocalBuffers(rel))
- {
- if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
- return true;
- }
-
- /*
- * If a partitioned table, check that each partition is safe for
- * modification in parallel-mode.
- */
- if (target_rel_partitions_max_parallel_hazard(rel, context))
- return true;
-
- /*
- * If there are any index expressions or index predicate, check that they
- * are parallel-mode safe.
- */
- if (target_rel_index_max_parallel_hazard(rel, context))
- return true;
-
- /*
- * If any triggers exist, check that they are parallel-safe.
- */
- if (target_rel_trigger_max_parallel_hazard(rel, context))
- return true;
-
- /*
- * Column default expressions are only applicable to INSERT and UPDATE.
- * For columns in the target-list, these are already being checked for
- * parallel-safety in the max_parallel_hazard() scan of the query tree in
- * standard_planner(), so there's no need to do it here. Note that even
- * though column defaults may be specified separately for each partition
- * in a partitioned table, a partition's default value is not applied when
- * inserting a tuple through a partitioned table.
- */
-
- /*
- * CHECK constraints are only applicable to INSERT and UPDATE. If any
- * CHECK constraints exist, determine if they are parallel-safe.
- */
- if (target_rel_chk_constr_max_parallel_hazard(rel, context))
- return true;
-
- return false;
-}
-
-/*
- * target_rel_trigger_max_parallel_hazard
- *
- * Finds the maximum parallel-mode hazard level for the specified relation's
- * trigger data.
- */
-static bool
-target_rel_trigger_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context)
-{
- int i;
-
- if (rel->trigdesc == NULL)
- return false;
-
- /*
- * Care is needed here to avoid using the same relcache TriggerDesc field
- * across other cache accesses, because relcache doesn't guarantee that it
- * won't move.
- */
- for (i = 0; i < rel->trigdesc->numtriggers; i++)
- {
- Oid tgfoid = rel->trigdesc->triggers[i].tgfoid;
-
- if (max_parallel_hazard_test(func_parallel(tgfoid), context))
- return true;
- }
-
- return false;
-}
-
-/*
- * target_rel_index_max_parallel_hazard
- *
- * Finds the maximum parallel-mode hazard level for any existing index
- * expressions or index predicate of a specified relation.
- */
-static bool
-target_rel_index_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context)
-{
- List *index_oid_list;
- ListCell *lc;
- bool found_max_hazard = false;
- LOCKMODE lockmode = AccessShareLock;
-
- index_oid_list = RelationGetIndexList(rel);
- foreach(lc, index_oid_list)
- {
- Relation index_rel;
- Form_pg_index indexStruct;
- List *ii_Expressions;
- List *ii_Predicate;
- Oid index_oid = lfirst_oid(lc);
-
- index_rel = index_open(index_oid, lockmode);
-
- indexStruct = index_rel->rd_index;
- ii_Expressions = RelationGetIndexExpressions(index_rel);
-
- if (ii_Expressions != NIL)
- {
- int i;
- ListCell *index_expr_item = list_head(ii_Expressions);
-
- for (i = 0; i < indexStruct->indnatts; i++)
- {
- int keycol = indexStruct->indkey.values[i];
-
- if (keycol == 0)
- {
- /* Found an index expression */
-
- Node *index_expr;
-
- Assert(index_expr_item != NULL);
- if (index_expr_item == NULL) /* shouldn't happen */
- {
- elog(WARNING, "too few entries in indexprs list");
- context->max_hazard = PROPARALLEL_UNSAFE;
- found_max_hazard = true;
- break;
- }
-
- index_expr = (Node *) lfirst(index_expr_item);
-
- if (max_parallel_hazard_walker(index_expr, context))
- {
- found_max_hazard = true;
- break;
- }
-
- index_expr_item = lnext(ii_Expressions, index_expr_item);
- }
- }
- }
-
- if (!found_max_hazard)
- {
- ii_Predicate = RelationGetIndexPredicate(index_rel);
- if (ii_Predicate != NIL)
- {
- if (max_parallel_hazard_walker((Node *) ii_Predicate, context))
- found_max_hazard = true;
- }
- }
-
- /*
- * XXX We don't need to retain lock on index as index expressions
- * can't be changed later.
- */
- index_close(index_rel, lockmode);
- }
- list_free(index_oid_list);
-
- return found_max_hazard;
-}
-
-/*
- * target_rel_domain_max_parallel_hazard
- *
- * Finds the maximum parallel-mode hazard level for the specified DOMAIN type.
- * Only any CHECK expressions are examined for parallel-safety.
- */
-static bool
-target_rel_domain_max_parallel_hazard(Oid typid, max_parallel_hazard_context *context)
-{
- Relation con_rel;
- ScanKeyData key[1];
- SysScanDesc scan;
- HeapTuple tup;
- bool found_max_hazard = false;
-
- LOCKMODE lockmode = AccessShareLock;
-
- con_rel = table_open(ConstraintRelationId, lockmode);
-
- ScanKeyInit(&key[0],
- Anum_pg_constraint_contypid, BTEqualStrategyNumber,
- F_OIDEQ, ObjectIdGetDatum(typid));
- scan = systable_beginscan(con_rel, ConstraintTypidIndexId, true,
- NULL, 1, key);
-
- while (HeapTupleIsValid((tup = systable_getnext(scan))))
- {
- Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
-
- if (con->contype == CONSTRAINT_CHECK)
- {
- char *conbin;
- Datum val;
- bool isnull;
- Expr *check_expr;
-
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conbin, &isnull);
- Assert(!isnull);
- if (isnull)
- {
- /*
- * This shouldn't ever happen, but if it does, log a WARNING
- * and return UNSAFE, rather than erroring out.
- */
- elog(WARNING, "null conbin for constraint %u", con->oid);
- context->max_hazard = PROPARALLEL_UNSAFE;
- found_max_hazard = true;
- break;
- }
- conbin = TextDatumGetCString(val);
- check_expr = stringToNode(conbin);
- pfree(conbin);
- if (max_parallel_hazard_walker((Node *) check_expr, context))
- {
- found_max_hazard = true;
- break;
- }
- }
- }
-
- systable_endscan(scan);
- table_close(con_rel, lockmode);
- return found_max_hazard;
-}
-
-/*
- * target_rel_partitions_max_parallel_hazard
- *
- * Finds the maximum parallel-mode hazard level for any partitions of a
- * of a specified relation.
- */
-static bool
-target_rel_partitions_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context)
-{
- int i;
- PartitionDesc pdesc;
- PartitionKey pkey;
- ListCell *partexprs_item;
- int partnatts;
- List *partexprs;
- PlannerGlobal *glob;
-
-
- if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
- return false;
-
- pkey = RelationGetPartitionKey(rel);
-
- partnatts = get_partition_natts(pkey);
- partexprs = get_partition_exprs(pkey);
-
- partexprs_item = list_head(partexprs);
- for (i = 0; i < partnatts; i++)
- {
- /* Check parallel-safety of partition key support functions */
- if (OidIsValid(pkey->partsupfunc[i].fn_oid))
- {
- if (max_parallel_hazard_test(func_parallel(pkey->partsupfunc[i].fn_oid), context))
- return true;
- }
-
- /* Check parallel-safety of any expressions in the partition key */
- if (get_partition_col_attnum(pkey, i) == 0)
- {
- Node *check_expr = (Node *) lfirst(partexprs_item);
-
- if (max_parallel_hazard_walker(check_expr, context))
- return true;
-
- partexprs_item = lnext(partexprs, partexprs_item);
- }
- }
-
- /* Recursively check each partition ... */
-
- /* Create the PartitionDirectory infrastructure if we didn't already */
- glob = context->planner_global;
- if (glob->partition_directory == NULL)
- glob->partition_directory =
- CreatePartitionDirectory(CurrentMemoryContext);
-
- pdesc = PartitionDirectoryLookup(glob->partition_directory, rel);
-
- for (i = 0; i < pdesc->nparts; i++)
- {
- bool max_hazard_found;
- Relation part_rel;
-
- /*
- * The partition needs to be locked, and remain locked until
- * end-of-transaction to ensure its parallel-safety state is not
- * hereafter altered.
- */
- part_rel = table_open(pdesc->oids[i], context->target_rte->rellockmode);
- max_hazard_found = target_rel_max_parallel_hazard_recurse(part_rel,
- context->command_type,
- context);
- table_close(part_rel, NoLock);
-
- /*
- * Remember partitionOids to record the partition as a potential plan
- * dependency.
- */
- glob->partitionOids = lappend_oid(glob->partitionOids, pdesc->oids[i]);
-
- if (max_hazard_found)
- return true;
- }
-
- return false;
-}
-
-/*
- * target_rel_chk_constr_max_parallel_hazard
- *
- * Finds the maximum parallel-mode hazard level for any CHECK expressions or
- * CHECK constraints related to the specified relation.
- */
-static bool
-target_rel_chk_constr_max_parallel_hazard(Relation rel,
- max_parallel_hazard_context *context)
-{
- TupleDesc tupdesc;
-
- tupdesc = RelationGetDescr(rel);
-
- /*
- * Determine if there are any CHECK constraints which are not
- * parallel-safe.
- */
- if (tupdesc->constr != NULL && tupdesc->constr->num_check > 0)
- {
- int i;
-
- ConstrCheck *check = tupdesc->constr->check;
-
- for (i = 0; i < tupdesc->constr->num_check; i++)
- {
- Expr *check_expr = stringToNode(check->ccbin);
-
- if (max_parallel_hazard_walker((Node *) check_expr, context))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * is_parallel_allowed_for_modify
- *
- * Check at a high-level if parallel mode is able to be used for the specified
- * table-modification statement. Currently, we support only Inserts.
- *
- * It's not possible in the following cases:
- *
- * 1) enable_parallel_insert is off
- * 2) INSERT...ON CONFLICT...DO UPDATE
- * 3) INSERT without SELECT
- * 4) the reloption parallel_insert_enabled is set to off
- *
- * (Note: we don't do in-depth parallel-safety checks here, we do only the
- * cheaper tests that can quickly exclude obvious cases for which
- * parallelism isn't supported, to avoid having to do further parallel-safety
- * checks for these)
- */
-bool
-is_parallel_allowed_for_modify(Query *parse)
-{
- bool hasSubQuery;
- bool parallel_enabled;
- RangeTblEntry *rte;
- ListCell *lc;
- Relation rel;
-
- if (!IsModifySupportedInParallelMode(parse->commandType))
- return false;
-
- if (!enable_parallel_insert)
- return false;
-
- /*
- * UPDATE is not currently supported in parallel-mode, so prohibit
- * INSERT...ON CONFLICT...DO UPDATE...
- *
- * In order to support update, even if only in the leader, some further
- * work would need to be done. A mechanism would be needed for sharing
- * combo-cids between leader and workers during parallel-mode, since for
- * example, the leader might generate a combo-cid and it needs to be
- * propagated to the workers.
- */
- if (parse->commandType == CMD_INSERT &&
- parse->onConflict != NULL &&
- parse->onConflict->action == ONCONFLICT_UPDATE)
- return false;
-
- /*
- * If there is no underlying SELECT, a parallel insert operation is not
- * desirable.
- */
- hasSubQuery = false;
- foreach(lc, parse->rtable)
- {
- rte = lfirst_node(RangeTblEntry, lc);
- if (rte->rtekind == RTE_SUBQUERY)
- {
- hasSubQuery = true;
- break;
- }
- }
-
- if (!hasSubQuery)
- return false;
-
- /*
- * Check if parallel_insert_enabled is enabled for the target table, if
- * not, skip the safety checks.
- *
- * (Note: if the target table is partitioned, the parallel_insert_enabled
- * option setting of the partitions are ignored).
- */
- rte = rt_fetch(parse->resultRelation, parse->rtable);
-
- /*
- * The target table is already locked by the caller (this is done in the
- * parse/analyze phase), and remains locked until end-of-transaction.
- */
- rel = table_open(rte->relid, NoLock);
-
- parallel_enabled = RelationGetParallelInsert(rel, true);
- table_close(rel, NoLock);
-
- return parallel_enabled;
-}
/*****************************************************************************
* Check clauses for nonstrict functions