Skip to content

[pull] master from postgres:master #105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions src/backend/commands/indexcmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -2592,7 +2592,9 @@ makeObjectName(const char *name1, const char *name2, const char *label)
* constraint names.)
*
* Note: it is theoretically possible to get a collision anyway, if someone
* else chooses the same name concurrently. This is fairly unlikely to be
* else chooses the same name concurrently. We shorten the race condition
* window by checking for conflicting relations using SnapshotDirty, but
* that doesn't close the window entirely. This is fairly unlikely to be
* a problem in practice, especially if one is holding an exclusive lock on
* the relation identified by name1. However, if choosing multiple names
* within a single command, you'd better create the new object and do
Expand All @@ -2608,15 +2610,45 @@ ChooseRelationName(const char *name1, const char *name2,
int pass = 0;
char *relname = NULL;
char modlabel[NAMEDATALEN];
SnapshotData SnapshotDirty;
Relation pgclassrel;

/* prepare to search pg_class with a dirty snapshot */
InitDirtySnapshot(SnapshotDirty);
pgclassrel = table_open(RelationRelationId, AccessShareLock);

/* try the unmodified label first */
strlcpy(modlabel, label, sizeof(modlabel));

for (;;)
{
ScanKeyData key[2];
SysScanDesc scan;
bool collides;

relname = makeObjectName(name1, name2, modlabel);

if (!OidIsValid(get_relname_relid(relname, namespaceid)))
/* is there any conflicting relation name? */
ScanKeyInit(&key[0],
Anum_pg_class_relname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(relname));
ScanKeyInit(&key[1],
Anum_pg_class_relnamespace,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(namespaceid));

scan = systable_beginscan(pgclassrel, ClassNameNspIndexId,
true /* indexOK */ ,
&SnapshotDirty,
2, key);

collides = HeapTupleIsValid(systable_getnext(scan));

systable_endscan(scan);

/* break out of loop if no conflict */
if (!collides)
{
if (!isconstraint ||
!ConstraintNameExists(relname, namespaceid))
Expand All @@ -2628,6 +2660,8 @@ ChooseRelationName(const char *name1, const char *name2,
snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
}

table_close(pgclassrel, AccessShareLock);

return relname;
}

Expand Down
9 changes: 3 additions & 6 deletions src/backend/optimizer/path/joinpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,16 +876,13 @@ try_nestloop_path(PlannerInfo *root,
/*
* Check to see if proposed path is still parameterized, and reject if the
* parameterization wouldn't be sensible --- unless allow_star_schema_join
* says to allow it anyway. Also, we must reject if have_dangerous_phv
* doesn't like the look of it, which could only happen if the nestloop is
* still parameterized.
* says to allow it anyway.
*/
required_outer = calc_nestloop_required_outer(outerrelids, outer_paramrels,
innerrelids, inner_paramrels);
if (required_outer &&
((!bms_overlap(required_outer, extra->param_source_rels) &&
!allow_star_schema_join(root, outerrelids, inner_paramrels)) ||
have_dangerous_phv(root, outerrelids, inner_paramrels)))
!bms_overlap(required_outer, extra->param_source_rels) &&
!allow_star_schema_join(root, outerrelids, inner_paramrels))
{
/* Waste no memory when we reject a path here */
bms_free(required_outer);
Expand Down
60 changes: 0 additions & 60 deletions src/backend/optimizer/path/joinrels.c
Original file line number Diff line number Diff line change
Expand Up @@ -565,9 +565,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
* Also, if the lateral reference is only indirect, we should reject
* the join; whatever rel(s) the reference chain goes through must be
* joined to first.
*
* Another case that might keep us from building a valid plan is the
* implementation restriction described by have_dangerous_phv().
*/
lateral_fwd = bms_overlap(rel1->relids, rel2->lateral_relids);
lateral_rev = bms_overlap(rel2->relids, rel1->lateral_relids);
Expand All @@ -584,9 +581,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
/* check there is a direct reference from rel2 to rel1 */
if (!bms_overlap(rel1->relids, rel2->direct_lateral_relids))
return false; /* only indirect refs, so reject */
/* check we won't have a dangerous PHV */
if (have_dangerous_phv(root, rel1->relids, rel2->lateral_relids))
return false; /* might be unable to handle required PHV */
}
else if (lateral_rev)
{
Expand All @@ -599,9 +593,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
/* check there is a direct reference from rel1 to rel2 */
if (!bms_overlap(rel2->relids, rel1->direct_lateral_relids))
return false; /* only indirect refs, so reject */
/* check we won't have a dangerous PHV */
if (have_dangerous_phv(root, rel2->relids, rel1->lateral_relids))
return false; /* might be unable to handle required PHV */
}

/*
Expand Down Expand Up @@ -1278,57 +1269,6 @@ has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel)
}


/*
* There's a pitfall for creating parameterized nestloops: suppose the inner
* rel (call it A) has a parameter that is a PlaceHolderVar, and that PHV's
* minimum eval_at set includes the outer rel (B) and some third rel (C).
* We might think we could create a B/A nestloop join that's parameterized by
* C. But we would end up with a plan in which the PHV's expression has to be
* evaluated as a nestloop parameter at the B/A join; and the executor is only
* set up to handle simple Vars as NestLoopParams. Rather than add complexity
* and overhead to the executor for such corner cases, it seems better to
* forbid the join. (Note that we can still make use of A's parameterized
* path with pre-joined B+C as the outer rel. have_join_order_restriction()
* ensures that we will consider making such a join even if there are not
* other reasons to do so.)
*
* So we check whether any PHVs used in the query could pose such a hazard.
* We don't have any simple way of checking whether a risky PHV would actually
* be used in the inner plan, and the case is so unusual that it doesn't seem
* worth working very hard on it.
*
* This needs to be checked in two places. If the inner rel's minimum
* parameterization would trigger the restriction, then join_is_legal() should
* reject the join altogether, because there will be no workable paths for it.
* But joinpath.c has to check again for every proposed nestloop path, because
* the inner path might have more than the minimum parameterization, causing
* some PHV to be dangerous for it that otherwise wouldn't be.
*/
bool
have_dangerous_phv(PlannerInfo *root,
Relids outer_relids, Relids inner_params)
{
ListCell *lc;

foreach(lc, root->placeholder_list)
{
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);

if (!bms_is_subset(phinfo->ph_eval_at, inner_params))
continue; /* ignore, could not be a nestloop param */
if (!bms_overlap(phinfo->ph_eval_at, outer_relids))
continue; /* ignore, not relevant to this join */
if (bms_is_subset(phinfo->ph_eval_at, outer_relids))
continue; /* safe, it can be eval'd within outerrel */
/* Otherwise, it's potentially unsafe, so reject the join */
return true;
}

/* OK to perform the join */
return false;
}


/*
* is_dummy_rel --- has relation been proven empty?
*/
Expand Down
46 changes: 43 additions & 3 deletions src/backend/optimizer/plan/createplan.c
Original file line number Diff line number Diff line change
Expand Up @@ -4348,9 +4348,11 @@ create_nestloop_plan(PlannerInfo *root,
List *joinrestrictclauses = best_path->jpath.joinrestrictinfo;
List *joinclauses;
List *otherclauses;
Relids outerrelids;
List *nestParams;
List *outer_tlist;
bool outer_parallel_safe;
Relids saveOuterRels = root->curOuterRels;
ListCell *lc;

/*
* If the inner path is parameterized by the topmost parent of the outer
Expand Down Expand Up @@ -4412,9 +4414,47 @@ create_nestloop_plan(PlannerInfo *root,
* Identify any nestloop parameters that should be supplied by this join
* node, and remove them from root->curOuterParams.
*/
outerrelids = best_path->jpath.outerjoinpath->parent->relids;
nestParams = identify_current_nestloop_params(root, outerrelids);
nestParams = identify_current_nestloop_params(root,
best_path->jpath.outerjoinpath);

/*
* While nestloop parameters that are Vars had better be available from
* the outer_plan already, there are edge cases where nestloop parameters
* that are PHVs won't be. In such cases we must add them to the
* outer_plan's tlist, since the executor's NestLoopParam machinery
* requires the params to be simple outer-Var references to that tlist.
*/
outer_tlist = outer_plan->targetlist;
outer_parallel_safe = outer_plan->parallel_safe;
foreach(lc, nestParams)
{
NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
TargetEntry *tle;

if (IsA(nlp->paramval, Var))
continue; /* nothing to do for simple Vars */
if (tlist_member((Expr *) nlp->paramval, outer_tlist))
continue; /* already available */

/* Make a shallow copy of outer_tlist, if we didn't already */
if (outer_tlist == outer_plan->targetlist)
outer_tlist = list_copy(outer_tlist);
/* ... and add the needed expression */
tle = makeTargetEntry((Expr *) copyObject(nlp->paramval),
list_length(outer_tlist) + 1,
NULL,
true);
outer_tlist = lappend(outer_tlist, tle);
/* ... and track whether tlist is (still) parallel-safe */
if (outer_parallel_safe)
outer_parallel_safe = is_parallel_safe(root,
(Node *) nlp->paramval);
}
if (outer_tlist != outer_plan->targetlist)
outer_plan = change_plan_targetlist(outer_plan, outer_tlist,
outer_parallel_safe);

/* And finally, we can build the join plan node */
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
Expand Down
39 changes: 28 additions & 11 deletions src/backend/optimizer/util/paramassign.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)

/*
* Identify any NestLoopParams that should be supplied by a NestLoop plan
* node with the specified lefthand rels. Remove them from the active
* node with the specified lefthand input path. Remove them from the active
* root->curOuterParams list and return them as the result list.
*
* XXX Here we also hack up the returned Vars and PHVs so that they do not
Expand All @@ -626,11 +626,26 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
* subquery, which'd be unduly expensive.
*/
List *
identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
identify_current_nestloop_params(PlannerInfo *root, Path *leftpath)
{
List *result;
Relids leftrelids = leftpath->parent->relids;
Relids outerrelids = PATH_REQ_OUTER(leftpath);
Relids allleftrelids;
ListCell *cell;

/*
* We'll be able to evaluate a PHV in the lefthand path if it uses the
* lefthand rels plus any available required-outer rels. But don't do so
* if it uses *only* required-outer rels; in that case it should be
* evaluated higher in the tree. For Vars, no such hair-splitting is
* necessary since they depend on only one relid.
*/
if (outerrelids)
allleftrelids = bms_union(leftrelids, outerrelids);
else
allleftrelids = leftrelids;

result = NIL;
foreach(cell, root->curOuterParams)
{
Expand All @@ -653,18 +668,20 @@ identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
leftrelids);
result = lappend(result, nlp);
}
else if (IsA(nlp->paramval, PlaceHolderVar) &&
bms_is_subset(find_placeholder_info(root,
(PlaceHolderVar *) nlp->paramval)->ph_eval_at,
leftrelids))
else if (IsA(nlp->paramval, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
Relids eval_at = find_placeholder_info(root, phv)->ph_eval_at;

root->curOuterParams = foreach_delete_current(root->curOuterParams,
cell);
phv->phnullingrels = bms_intersect(phv->phnullingrels,
leftrelids);
result = lappend(result, nlp);
if (bms_is_subset(eval_at, allleftrelids) &&
bms_overlap(eval_at, leftrelids))
{
root->curOuterParams = foreach_delete_current(root->curOuterParams,
cell);
phv->phnullingrels = bms_intersect(phv->phnullingrels,
leftrelids);
result = lappend(result, nlp);
}
}
}
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/include/optimizer/paramassign.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extern Param *replace_nestloop_param_placeholdervar(PlannerInfo *root,
extern void process_subquery_nestloop_params(PlannerInfo *root,
List *subplan_params);
extern List *identify_current_nestloop_params(PlannerInfo *root,
Relids leftrelids);
Path *leftpath);
extern Param *generate_new_exec_param(PlannerInfo *root, Oid paramtype,
int32 paramtypmod, Oid paramcollation);
extern int assign_special_exec_param(PlannerInfo *root);
Expand Down
2 changes: 0 additions & 2 deletions src/include/optimizer/paths.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ extern Relids add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
List **pushed_down_joins);
extern bool have_join_order_restriction(PlannerInfo *root,
RelOptInfo *rel1, RelOptInfo *rel2);
extern bool have_dangerous_phv(PlannerInfo *root,
Relids outer_relids, Relids inner_params);
extern void mark_dummy_rel(RelOptInfo *rel);
extern void init_dummy_sjinfo(SpecialJoinInfo *sjinfo, Relids left_relids,
Relids right_relids);
Expand Down
Loading