diff options
Diffstat (limited to 'src/backend/optimizer')
| -rw-r--r-- | src/backend/optimizer/path/costsize.c | 26 | ||||
| -rw-r--r-- | src/backend/optimizer/path/joinpath.c | 21 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/createplan.c | 26 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/subselect.c | 45 | ||||
| -rw-r--r-- | src/backend/optimizer/util/pathnode.c | 12 | ||||
| -rw-r--r-- | src/backend/optimizer/util/plancat.c | 5 |
6 files changed, 57 insertions, 78 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 94077e6a006..8335cf5b5c5 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -257,32 +257,6 @@ clamp_width_est(int64 tuple_width) return (int32) tuple_width; } -/* - * clamp_cardinality_to_long - * Cast a Cardinality value to a sane long value. - */ -long -clamp_cardinality_to_long(Cardinality x) -{ - /* - * Just for paranoia's sake, ensure we do something sane with negative or - * NaN values. - */ - if (isnan(x)) - return LONG_MAX; - if (x <= 0) - return 0; - - /* - * If "long" is 64 bits, then LONG_MAX cannot be represented exactly as a - * double. Casting it to double and back may well result in overflow due - * to rounding, so avoid doing that. We trust that any double value that - * compares strictly less than "(double) LONG_MAX" will cast to a - * representable "long" value. - */ - return (x < (double) LONG_MAX) ? (long) x : LONG_MAX; -} - /* * cost_seqscan diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 3b9407eb2eb..ea5b6415186 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -2260,10 +2260,20 @@ hash_inner_and_outer(PlannerInfo *root, /* * If the joinrel is parallel-safe, we may be able to consider a - * partial hash join. However, the resulting path must not be - * parameterized. + * partial hash join. + * + * However, we can't handle JOIN_RIGHT_SEMI, because the hash table is + * either a shared hash table or a private hash table per backend. In + * the shared case, there is no concurrency protection for the match + * flags, so multiple workers could inspect and set the flags + * concurrently, potentially producing incorrect results. In the + * private case, each worker has its own copy of the hash table, so no + * single process has all the match flags. + * + * Also, the resulting path must not be parameterized. */ if (joinrel->consider_parallel && + jointype != JOIN_RIGHT_SEMI && outerrel->partial_pathlist != NIL && bms_is_empty(joinrel->lateral_relids)) { @@ -2294,13 +2304,12 @@ hash_inner_and_outer(PlannerInfo *root, * Normally, given that the joinrel is parallel-safe, the cheapest * total inner path will also be parallel-safe, but if not, we'll * have to search for the cheapest safe, unparameterized inner - * path. If full, right, right-semi or right-anti join, we can't - * use parallelism (building the hash table in each backend) - * because no one process has all the match bits. + * path. If full, right, or right-anti join, we can't use + * parallelism (building the hash table in each backend) because + * no one process has all the match bits. */ if (jointype == JOIN_FULL || jointype == JOIN_RIGHT || - jointype == JOIN_RIGHT_SEMI || jointype == JOIN_RIGHT_ANTI) cheapest_safe_inner = NULL; else if (cheapest_total_inner->parallel_safe) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 63fe6637155..8af091ba647 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -226,7 +226,7 @@ static RecursiveUnion *make_recursive_union(List *tlist, Plan *righttree, int wtParam, List *distinctList, - long numGroups); + Cardinality numGroups); static BitmapAnd *make_bitmap_and(List *bitmapplans); static BitmapOr *make_bitmap_or(List *bitmapplans); static NestLoop *make_nestloop(List *tlist, @@ -301,7 +301,7 @@ static Gather *make_gather(List *qptlist, List *qpqual, int nworkers, int rescan_param, bool single_copy, Plan *subplan); static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, List *tlist, Plan *lefttree, Plan *righttree, - List *groupList, long numGroups); + List *groupList, Cardinality numGroups); static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam); static Result *make_gating_result(List *tlist, Node *resconstantqual, Plan *subplan); @@ -2564,7 +2564,6 @@ create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags) List *tlist = build_path_tlist(root, &best_path->path); Plan *leftplan; Plan *rightplan; - long numGroups; /* * SetOp doesn't project, so tlist requirements pass through; moreover we @@ -2575,16 +2574,13 @@ create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags) rightplan = create_plan_recurse(root, best_path->rightpath, flags | CP_LABEL_TLIST); - /* Convert numGroups to long int --- but 'ware overflow! */ - numGroups = clamp_cardinality_to_long(best_path->numGroups); - plan = make_setop(best_path->cmd, best_path->strategy, tlist, leftplan, rightplan, best_path->groupList, - numGroups); + best_path->numGroups); copy_generic_path_info(&plan->plan, (Path *) best_path); @@ -2604,7 +2600,6 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path) Plan *leftplan; Plan *rightplan; List *tlist; - long numGroups; /* Need both children to produce same tlist, so force it */ leftplan = create_plan_recurse(root, best_path->leftpath, CP_EXACT_TLIST); @@ -2612,15 +2607,12 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path) tlist = build_path_tlist(root, &best_path->path); - /* Convert numGroups to long int --- but 'ware overflow! */ - numGroups = clamp_cardinality_to_long(best_path->numGroups); - plan = make_recursive_union(tlist, leftplan, rightplan, best_path->wtParam, best_path->distinctList, - numGroups); + best_path->numGroups); copy_generic_path_info(&plan->plan, (Path *) best_path); @@ -5845,7 +5837,7 @@ make_recursive_union(List *tlist, Plan *righttree, int wtParam, List *distinctList, - long numGroups) + Cardinality numGroups) { RecursiveUnion *node = makeNode(RecursiveUnion); Plan *plan = &node->plan; @@ -6582,15 +6574,11 @@ Agg * make_agg(List *tlist, List *qual, AggStrategy aggstrategy, AggSplit aggsplit, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations, - List *groupingSets, List *chain, double dNumGroups, + List *groupingSets, List *chain, Cardinality numGroups, Size transitionSpace, Plan *lefttree) { Agg *node = makeNode(Agg); Plan *plan = &node->plan; - long numGroups; - - /* Reduce to long, but 'ware overflow! */ - numGroups = clamp_cardinality_to_long(dNumGroups); node->aggstrategy = aggstrategy; node->aggsplit = aggsplit; @@ -6822,7 +6810,7 @@ make_gather(List *qptlist, static SetOp * make_setop(SetOpCmd cmd, SetOpStrategy strategy, List *tlist, Plan *lefttree, Plan *righttree, - List *groupList, long numGroups) + List *groupList, Cardinality numGroups) { SetOp *node = makeNode(SetOp); Plan *plan = &node->plan; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 14192a13236..ff63d20f8d5 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -20,6 +20,7 @@ #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "executor/executor.h" +#include "executor/nodeSubplan.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -79,8 +80,8 @@ static Node *convert_testexpr(PlannerInfo *root, List *subst_nodes); static Node *convert_testexpr_mutator(Node *node, convert_testexpr_context *context); -static bool subplan_is_hashable(Plan *plan); -static bool subpath_is_hashable(Path *path); +static bool subplan_is_hashable(Plan *plan, bool unknownEqFalse); +static bool subpath_is_hashable(Path *path, bool unknownEqFalse); static bool testexpr_is_hashable(Node *testexpr, List *param_ids); static bool test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids); static bool hash_ok_operator(OpExpr *expr); @@ -283,7 +284,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, best_path = final_rel->cheapest_total_path; /* Now we can check if it'll fit in hash_mem */ - if (subpath_is_hashable(best_path)) + if (subpath_is_hashable(best_path, true)) { SubPlan *hashplan; AlternativeSubPlan *asplan; @@ -524,7 +525,7 @@ build_subplan(PlannerInfo *root, Plan *plan, Path *path, */ if (subLinkType == ANY_SUBLINK && splan->parParam == NIL && - subplan_is_hashable(plan) && + subplan_is_hashable(plan, unknownEqFalse) && testexpr_is_hashable(splan->testexpr, splan->paramIds)) splan->useHashTable = true; @@ -711,19 +712,19 @@ convert_testexpr_mutator(Node *node, * is suitable for hashing. We only look at the subquery itself. */ static bool -subplan_is_hashable(Plan *plan) +subplan_is_hashable(Plan *plan, bool unknownEqFalse) { - double subquery_size; + Size hashtablesize; /* - * The estimated size of the subquery result must fit in hash_mem. (Note: - * we use heap tuple overhead here even though the tuples will actually be - * stored as MinimalTuples; this provides some fudge factor for hashtable - * overhead.) + * The estimated size of the hashtable holding the subquery result must + * fit in hash_mem. (Note: reject on equality, to ensure that an estimate + * of SIZE_MAX disables hashing regardless of the hash_mem limit.) */ - subquery_size = plan->plan_rows * - (MAXALIGN(plan->plan_width) + MAXALIGN(SizeofHeapTupleHeader)); - if (subquery_size > get_hash_memory_limit()) + hashtablesize = EstimateSubplanHashTableSpace(plan->plan_rows, + plan->plan_width, + unknownEqFalse); + if (hashtablesize >= get_hash_memory_limit()) return false; return true; @@ -735,19 +736,19 @@ subplan_is_hashable(Plan *plan) * Identical to subplan_is_hashable, but work from a Path for the subplan. */ static bool -subpath_is_hashable(Path *path) +subpath_is_hashable(Path *path, bool unknownEqFalse) { - double subquery_size; + Size hashtablesize; /* - * The estimated size of the subquery result must fit in hash_mem. (Note: - * we use heap tuple overhead here even though the tuples will actually be - * stored as MinimalTuples; this provides some fudge factor for hashtable - * overhead.) + * The estimated size of the hashtable holding the subquery result must + * fit in hash_mem. (Note: reject on equality, to ensure that an estimate + * of SIZE_MAX disables hashing regardless of the hash_mem limit.) */ - subquery_size = path->rows * - (MAXALIGN(path->pathtarget->width) + MAXALIGN(SizeofHeapTupleHeader)); - if (subquery_size > get_hash_memory_limit()) + hashtablesize = EstimateSubplanHashTableSpace(path->rows, + path->pathtarget->width, + unknownEqFalse); + if (hashtablesize >= get_hash_memory_limit()) return false; return true; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 44ac5312edd..e4fd6950fad 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -17,6 +17,7 @@ #include <math.h> #include "access/htup_details.h" +#include "executor/nodeSetOp.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/extensible.h" @@ -3461,7 +3462,7 @@ create_setop_path(PlannerInfo *root, } else { - Size hashentrysize; + Size hashtablesize; /* * In hashed mode, we must read all the input before we can emit @@ -3490,11 +3491,12 @@ create_setop_path(PlannerInfo *root, /* * Also disable if it doesn't look like the hashtable will fit into - * hash_mem. + * hash_mem. (Note: reject on equality, to ensure that an estimate of + * SIZE_MAX disables hashing regardless of the hash_mem limit.) */ - hashentrysize = MAXALIGN(leftpath->pathtarget->width) + - MAXALIGN(SizeofMinimalTupleHeader); - if (hashentrysize * numGroups > get_hash_memory_limit()) + hashtablesize = EstimateSetOpHashTableSpace(numGroups, + leftpath->pathtarget->width); + if (hashtablesize >= get_hash_memory_limit()) pathnode->path.disabled_nodes++; } pathnode->path.rows = outputRows; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index f4b7343dace..d950bd93002 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -789,6 +789,11 @@ find_relation_notnullatts(PlannerInfo *root, Oid relid) * the purposes of inference. If no opclass (or collation) is specified, then * all matching indexes (that may or may not match the default in terms of * each attribute opclass/collation) are used for inference. + * + * Note: during index CONCURRENTLY operations, different transactions may + * reference different sets of arbiter indexes. This can lead to false unique + * constraint violations that wouldn't occur during normal operations. For + * more information, see insert.sgml. */ List * infer_arbiter_indexes(PlannerInfo *root) |
