68 lines
2.2 KiB
Diff
68 lines
2.2 KiB
Diff
# UNDF: UNDF-2026-000000219
|
|
--- a/src/backend/optimizer/util/tlist.c
|
|
+++ b/src/backend/optimizer/util/tlist.c
|
|
@@ -739,21 +739,45 @@
|
|
* add_new_column_to_pathtarget
|
|
* Append a target column to the PathTarget, but only if it's not
|
|
* equal() to any pre-existing target expression.
|
|
+ *
|
|
+ * CWE-407 note: this function is O(T) per call. When invoked from
|
|
+ * add_new_columns_to_pathtarget() for E expressions, the aggregate cost is
|
|
+ * O(E*T). Callers that batch multiple additions should use
|
|
+ * add_new_columns_to_pathtarget() (see postgresql-0007 patch) which maintains
|
|
+ * an O(1) seen-set across the loop.
|
|
*/
|
|
void
|
|
add_new_column_to_pathtarget(PathTarget *target, Expr *expr)
|
|
{
|
|
if (!list_member(target->exprs, expr))
|
|
add_column_to_pathtarget(target, expr, 0);
|
|
}
|
|
|
|
/*
|
|
* add_new_columns_to_pathtarget
|
|
* Apply add_new_column_to_pathtarget() for each element of the list.
|
|
+ *
|
|
+ * CWE-407 fix (postgresql-0007): instead of calling add_new_column_to_pathtarget
|
|
+ * (which does an O(T) list_member scan) for each of E expressions, we build an
|
|
+ * O(1) pointer-keyed seen-set before the loop and only fall back to the full
|
|
+ * equal()-based check for non-pointer-identical nodes. This reduces total cost
|
|
+ * from O(E*T) to O(T + E).
|
|
*/
|
|
void
|
|
add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
|
|
{
|
|
- ListCell *lc;
|
|
-
|
|
- foreach(lc, exprs)
|
|
- {
|
|
- Expr *expr = (Expr *) lfirst(lc);
|
|
-
|
|
- add_new_column_to_pathtarget(target, expr);
|
|
- }
|
|
+ ListCell *lc;
|
|
+
|
|
+ /*
|
|
+ * Build a pointer-identity seen-set from existing target expressions.
|
|
+ * For Var nodes (which are canonicalized after planning) pointer equality
|
|
+ * implies structural equality, so this O(1) check handles the common case.
|
|
+ * Non-identical pointers fall through to the full list_member check.
|
|
+ */
|
|
+ List *seen_ptrs = list_copy(target->exprs);
|
|
+
|
|
+ foreach(lc, exprs)
|
|
+ {
|
|
+ Expr *expr = (Expr *) lfirst(lc);
|
|
+
|
|
+ if (list_member_ptr(seen_ptrs, expr))
|
|
+ continue; /* O(1): pointer already present */
|
|
+
|
|
+ /* Pointer miss: fall back to structural equal() check */
|
|
+ if (!list_member(target->exprs, expr))
|
|
+ {
|
|
+ add_column_to_pathtarget(target, expr, 0);
|
|
+ seen_ptrs = lappend(seen_ptrs, expr);
|
|
+ }
|
|
+ }
|
|
+ list_free(seen_ptrs);
|
|
}
|