84 lines
2.7 KiB
Diff
84 lines
2.7 KiB
Diff
# UNDF: UNDF-2026-000000220
|
|
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
|
|
index abcdef..123456 100644
|
|
--- a/src/backend/optimizer/path/joinpath.c
|
|
+++ b/src/backend/optimizer/path/joinpath.c
|
|
@@ -481,6 +481,13 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
|
{
|
|
List *lateral_vars;
|
|
ListCell *lc;
|
|
+ /*
|
|
+ * CWE-407 fix (postgresql-0008, joinpath.c):
|
|
+ * Track seen Var exprs via a Bitmapset (O(1) per check) instead of
|
|
+ * list_member(*param_exprs, ...) which is O(|param_exprs|) and makes
|
|
+ * the full deduplication loop O(N²). Non-Var nodes fall back to a
|
|
+ * kept list and structural equal() — they are rare in practice.
|
|
+ */
|
|
+ Bitmapset *seen_bms = NULL;
|
|
+ List *seen_nonvar = NIL;
|
|
|
|
*param_exprs = NIL;
|
|
*operators = NIL;
|
|
@@ -534,9 +541,27 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
|
* 'expr' may already exist as a parameter from a previous item in
|
|
* ppi_clauses. No need to include it again, however we'd better
|
|
* ensure we do switch into binary mode if required. See below.
|
|
*/
|
|
- if (!list_member(*param_exprs, expr))
|
|
+ {
|
|
+ bool already_seen;
|
|
+
|
|
+ if (IsA(expr, Var))
|
|
+ {
|
|
+ Var *v = (Var *) expr;
|
|
+ int key = v->varno * 3200 + v->varattno + 1600;
|
|
+
|
|
+ already_seen = bms_is_member(key, seen_bms);
|
|
+ if (!already_seen)
|
|
+ seen_bms = bms_add_member(seen_bms, key);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ already_seen = list_member(seen_nonvar, expr);
|
|
+ if (!already_seen)
|
|
+ seen_nonvar = lappend(seen_nonvar, expr);
|
|
+ }
|
|
+ if (!already_seen)
|
|
{
|
|
*operators = lappend_oid(*operators, hasheqoperator);
|
|
*param_exprs = lappend(*param_exprs, expr);
|
|
}
|
|
+ }
|
|
|
|
/*
|
|
* When the join operator is not hashable then it's possible that
|
|
@@ -584,9 +599,24 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
|
* 'expr' may already exist as a parameter from the ppi_clauses. No
|
|
* need to include it again, however we'd better ensure we do switch
|
|
* into binary mode.
|
|
*/
|
|
- if (!list_member(*param_exprs, expr))
|
|
+ {
|
|
+ bool already_seen;
|
|
+
|
|
+ if (IsA(expr, Var))
|
|
+ {
|
|
+ Var *v = (Var *) expr;
|
|
+ int key = v->varno * 3200 + v->varattno + 1600;
|
|
+
|
|
+ already_seen = bms_is_member(key, seen_bms);
|
|
+ if (!already_seen)
|
|
+ seen_bms = bms_add_member(seen_bms, key);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ already_seen = list_member(seen_nonvar, expr);
|
|
+ if (!already_seen)
|
|
+ seen_nonvar = lappend(seen_nonvar, expr);
|
|
+ }
|
|
+ if (!already_seen)
|
|
{
|
|
*operators = lappend_oid(*operators, typentry->eq_opr);
|
|
*param_exprs = lappend(*param_exprs, expr);
|
|
}
|
|
+ }
|