summaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/costsize.c26
-rw-r--r--src/backend/optimizer/path/joinpath.c21
-rw-r--r--src/backend/optimizer/plan/createplan.c26
-rw-r--r--src/backend/optimizer/plan/subselect.c45
-rw-r--r--src/backend/optimizer/util/pathnode.c12
-rw-r--r--src/backend/optimizer/util/plancat.c5
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)