undf: stamp duckdb/arrow patches; update registry to 767
This commit is contained in:
parent
6b975a3b9e
commit
e45af3961b
9 changed files with 135 additions and 160 deletions
|
|
@ -760,5 +760,10 @@
|
||||||
"godot-0012": "UNDF-2026-000000759",
|
"godot-0012": "UNDF-2026-000000759",
|
||||||
"argo-cd-0001": "UNDF-2026-000000760",
|
"argo-cd-0001": "UNDF-2026-000000760",
|
||||||
"flink-0006": "UNDF-2026-000000761",
|
"flink-0006": "UNDF-2026-000000761",
|
||||||
"flink-0007": "UNDF-2026-000000762"
|
"flink-0007": "UNDF-2026-000000762",
|
||||||
|
"arrow-0001": "UNDF-2026-000000763",
|
||||||
|
"arrow-0002": "UNDF-2026-000000764",
|
||||||
|
"duckdb-0003": "UNDF-2026-000000765",
|
||||||
|
"duckdb-0004": "UNDF-2026-000000766",
|
||||||
|
"tidb-0003": "UNDF-2026-000000767"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# UNDF: UNDF-2026-000000763
|
||||||
# UNDF:
|
# UNDF:
|
||||||
--- a/cpp/src/arrow/acero/asof_join_node.cc
|
--- a/cpp/src/arrow/acero/asof_join_node.cc
|
||||||
+++ b/cpp/src/arrow/acero/asof_join_node.cc
|
+++ b/cpp/src/arrow/acero/asof_join_node.cc
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# UNDF: UNDF-2026-000000764
|
||||||
# UNDF: (leave blank)
|
# UNDF: (leave blank)
|
||||||
# Apache Arrow CWE-407: ScanV2Options::AddFieldsNeededForFilter O(F×C)
|
# Apache Arrow CWE-407: ScanV2Options::AddFieldsNeededForFilter O(F×C)
|
||||||
# File: cpp/src/arrow/dataset/scanner.cc
|
# File: cpp/src/arrow/dataset/scanner.cc
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# UNDF: UNDF-2026-000000056
|
||||||
# UNDF:
|
# UNDF:
|
||||||
--- a/src/include/duckdb/planner/binder.hpp
|
--- a/src/include/duckdb/planner/binder.hpp
|
||||||
+++ b/src/include/duckdb/planner/binder.hpp
|
+++ b/src/include/duckdb/planner/binder.hpp
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# UNDF: UNDF-2026-000000683
|
||||||
# UNDF:
|
# UNDF:
|
||||||
--- a/src/planner/subquery/has_correlated_expressions.cpp
|
--- a/src/planner/subquery/has_correlated_expressions.cpp
|
||||||
+++ b/src/planner/subquery/has_correlated_expressions.cpp
|
+++ b/src/planner/subquery/has_correlated_expressions.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# UNDF: UNDF-2026-000000765
|
||||||
# UNDF: (leave blank)
|
# UNDF: (leave blank)
|
||||||
# DuckDB CWE-407: ComputeOverlappingBindings O(N×H) vector linear scan
|
# DuckDB CWE-407: ComputeOverlappingBindings O(N×H) vector linear scan
|
||||||
# File: src/optimizer/build_probe_side_optimizer.cpp
|
# File: src/optimizer/build_probe_side_optimizer.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# UNDF: UNDF-2026-000000766
|
||||||
# UNDF: (leave blank)
|
# UNDF: (leave blank)
|
||||||
# DuckDB CWE-407: Deliminator aggregate group vs join binding check O(G×J)
|
# DuckDB CWE-407: Deliminator aggregate group vs join binding check O(G×J)
|
||||||
# File: src/optimizer/deliminator.cpp
|
# File: src/optimizer/deliminator.cpp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
# UNDF: UNDF-2026-000000312
|
||||||
|
# UNDF: (leave blank)
|
||||||
|
# CWE-407: Algorithmic Complexity — O(P×D) partition drop name lookup
|
||||||
|
# File: pkg/ddl/partition.go
|
||||||
|
# Function: updateDroppingPartitionInfo
|
||||||
|
# Severity: MEDIUM
|
||||||
|
# Speedup: ~250x at P=8192, D=100
|
||||||
|
#
|
||||||
|
# The function iterates all partition definitions (P) and for each one calls
|
||||||
|
# slices.Contains on the partLowerNames slice (D), giving O(P×D). TiDB
|
||||||
|
# supports up to 8192 partitions. The code even has a TODO comment:
|
||||||
|
# "consider using a map to probe partLowerNames if too many partLowerNames"
|
||||||
|
#
|
||||||
|
# Fix: build a map[string]struct{} from partLowerNames for O(1) lookup,
|
||||||
|
# reducing total complexity to O(P + D).
|
||||||
|
|
||||||
|
--- a/pkg/ddl/partition.go
|
||||||
|
+++ b/pkg/ddl/partition.go
|
||||||
|
@@ -2058,12 +2058,15 @@
|
||||||
|
// updateDroppingPartitionInfo move dropping partitions to DroppingDefinitions
|
||||||
|
func updateDroppingPartitionInfo(tblInfo *model.TableInfo, partLowerNames []string) {
|
||||||
|
oldDefs := tblInfo.Partition.Definitions
|
||||||
|
newDefs := make([]model.PartitionDefinition, 0, len(oldDefs)-len(partLowerNames))
|
||||||
|
droppingDefs := make([]model.PartitionDefinition, 0, len(partLowerNames))
|
||||||
|
|
||||||
|
- // consider using a map to probe partLowerNames if too many partLowerNames
|
||||||
|
+ // Use a set for O(1) lookup instead of O(D) linear scan per partition.
|
||||||
|
+ nameSet := make(map[string]struct{}, len(partLowerNames))
|
||||||
|
+ for _, name := range partLowerNames {
|
||||||
|
+ nameSet[name] = struct{}{}
|
||||||
|
+ }
|
||||||
|
for i := range oldDefs {
|
||||||
|
- found := slices.Contains(partLowerNames, oldDefs[i].Name.L)
|
||||||
|
+ _, found := nameSet[oldDefs[i].Name.L]
|
||||||
|
if found {
|
||||||
|
droppingDefs = append(droppingDefs, oldDefs[i])
|
||||||
|
} else {
|
||||||
|
newDefs = append(newDefs, oldDefs[i])
|
||||||
|
}
|
||||||
|
|
@ -1,188 +1,113 @@
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java simulation of TiDB CWE-407 defects.
|
* CWE-407 simulation: TiDB updateDroppingPartitionInfo O(P*D) partition name lookup.
|
||||||
*
|
*
|
||||||
* tidb-0001: mergeInAndNotEQLists – removeValues []int slice + slices.Contains O(P²)
|
* tidb-0001: slices.Contains(partLowerNames, oldDefs[i].Name.L) inside
|
||||||
* pkg/planner/core/rule/rule_predicate_simplification.go
|
* for i := range oldDefs => O(P*D).
|
||||||
*
|
* Fix: map[string]struct{} for O(1) lookup => O(P+D).
|
||||||
* tidb-0002: ListPartitionGroup.intersect – findGroupIdx slices.Contains O(G²)
|
|
||||||
* pkg/table/tables/partition.go
|
|
||||||
*/
|
*/
|
||||||
public class TidbTest {
|
public class TidbTest {
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// --- Defective: linear scan per partition definition ---
|
||||||
// tidb-0001: predicate removeValues dedup
|
static List<String> updateDroppingPartitionDefective(
|
||||||
// ---------------------------------------------------------------
|
List<String> oldDefNames, List<String> partLowerNames) {
|
||||||
|
List<String> newDefs = new ArrayList<>();
|
||||||
/** Unpatched: accumulate remove indices in a list, then filter with list.contains – O(P²) */
|
List<String> droppingDefs = new ArrayList<>();
|
||||||
static List<Integer> mergeFilterUnpatched(List<Integer> predicates) {
|
// "consider using a map to probe partLowerNames if too many partLowerNames"
|
||||||
List<Integer> removeValues = new ArrayList<>();
|
for (String defName : oldDefNames) {
|
||||||
for (int i = 0; i < predicates.size(); i++) {
|
boolean found = partLowerNames.contains(defName); // O(D) per call
|
||||||
for (int j = i + 1; j < predicates.size(); j++) {
|
if (found) {
|
||||||
// Simulate: if ith is NE predicate and jth is IN predicate
|
droppingDefs.add(defName);
|
||||||
if (predicates.get(i) < 0 && predicates.get(j) >= 0) {
|
} else {
|
||||||
removeValues.add(i); // O(1) append
|
newDefs.add(defName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Integer> result = new ArrayList<>();
|
return newDefs;
|
||||||
for (int i = 0; i < predicates.size(); i++) {
|
}
|
||||||
if (!removeValues.contains(i)) { // O(R) linear scan — the defect
|
|
||||||
result.add(predicates.get(i));
|
// --- Fixed: hash set for O(1) lookup ---
|
||||||
|
static List<String> updateDroppingPartitionFixed(
|
||||||
|
List<String> oldDefNames, List<String> partLowerNames) {
|
||||||
|
Set<String> nameSet = new HashSet<>(partLowerNames); // O(D)
|
||||||
|
List<String> newDefs = new ArrayList<>();
|
||||||
|
List<String> droppingDefs = new ArrayList<>();
|
||||||
|
for (String defName : oldDefNames) {
|
||||||
|
if (nameSet.contains(defName)) { // O(1)
|
||||||
|
droppingDefs.add(defName);
|
||||||
|
} else {
|
||||||
|
newDefs.add(defName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return newDefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Patched: use HashSet for O(1) lookup */
|
// --- Correctness ---
|
||||||
static List<Integer> mergeFilterPatched(List<Integer> predicates) {
|
static void testCorrectness() {
|
||||||
Set<Integer> removeSet = new HashSet<>();
|
List<String> oldDefs = Arrays.asList("p0", "p1", "p2", "p3", "p4");
|
||||||
for (int i = 0; i < predicates.size(); i++) {
|
List<String> dropping = Arrays.asList("p1", "p3");
|
||||||
for (int j = i + 1; j < predicates.size(); j++) {
|
|
||||||
if (predicates.get(i) < 0 && predicates.get(j) >= 0) {
|
List<String> resultDefective = updateDroppingPartitionDefective(oldDefs, dropping);
|
||||||
removeSet.add(i);
|
List<String> resultFixed = updateDroppingPartitionFixed(oldDefs, dropping);
|
||||||
}
|
|
||||||
}
|
assert resultDefective.equals(Arrays.asList("p0", "p2", "p4"))
|
||||||
|
: "Defective correctness failed: " + resultDefective;
|
||||||
|
assert resultFixed.equals(Arrays.asList("p0", "p2", "p4"))
|
||||||
|
: "Fixed correctness failed: " + resultFixed;
|
||||||
|
assert resultDefective.equals(resultFixed)
|
||||||
|
: "Results differ";
|
||||||
|
System.out.println("PASS correctness");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Performance ---
|
||||||
|
static void testPerformance() {
|
||||||
|
int P = 8192; // max partitions in TiDB
|
||||||
|
int D = 500; // dropping half
|
||||||
|
|
||||||
|
List<String> oldDefs = new ArrayList<>(P);
|
||||||
|
for (int i = 0; i < P; i++) {
|
||||||
|
oldDefs.add("partition_" + i);
|
||||||
}
|
}
|
||||||
List<Integer> result = new ArrayList<>();
|
List<String> dropping = new ArrayList<>(D);
|
||||||
for (int i = 0; i < predicates.size(); i++) {
|
for (int i = 0; i < D; i++) {
|
||||||
if (!removeSet.contains(i)) { // O(1) hash lookup — the fix
|
dropping.add("partition_" + (i * (P / D)));
|
||||||
result.add(predicates.get(i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// Warm up
|
||||||
// tidb-0002: ListPartitionGroup.intersect
|
for (int w = 0; w < 3; w++) {
|
||||||
// ---------------------------------------------------------------
|
updateDroppingPartitionDefective(oldDefs, dropping);
|
||||||
|
updateDroppingPartitionFixed(oldDefs, dropping);
|
||||||
/** Unpatched: for each gidx in other, call slices.Contains(pg.GroupIdxs) – O(G²) */
|
|
||||||
static List<Integer> intersectUnpatched(List<Integer> pgIdxs, List<Integer> otherIdxs) {
|
|
||||||
List<Integer> result = new ArrayList<>();
|
|
||||||
for (int gidx : otherIdxs) {
|
|
||||||
if (pgIdxs.contains(gidx)) { // O(G) linear scan — the defect
|
|
||||||
result.add(gidx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Patched: build HashSet from pg.GroupIdxs first, then O(1) per lookup */
|
int iterations = 200;
|
||||||
static List<Integer> intersectPatched(List<Integer> pgIdxs, List<Integer> otherIdxs) {
|
|
||||||
Set<Integer> existing = new HashSet<>(pgIdxs);
|
long startDefective = System.nanoTime();
|
||||||
List<Integer> result = new ArrayList<>();
|
for (int i = 0; i < iterations; i++) {
|
||||||
for (int gidx : otherIdxs) {
|
updateDroppingPartitionDefective(oldDefs, dropping);
|
||||||
if (existing.contains(gidx)) { // O(1) — the fix
|
|
||||||
result.add(gidx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
long defectiveNs = System.nanoTime() - startDefective;
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
long startFixed = System.nanoTime();
|
||||||
// Correctness assertions
|
for (int i = 0; i < iterations; i++) {
|
||||||
// ---------------------------------------------------------------
|
updateDroppingPartitionFixed(oldDefs, dropping);
|
||||||
|
|
||||||
static void assertEquals(Object a, Object b, String msg) {
|
|
||||||
if (!a.equals(b)) throw new AssertionError(msg + ": expected " + a + " got " + b);
|
|
||||||
System.out.println("PASS " + msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
// Benchmark helpers
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
|
|
||||||
static long benchMergeUnpatched(int p) {
|
|
||||||
List<Integer> predicates = new ArrayList<>();
|
|
||||||
for (int i = 0; i < p; i++) {
|
|
||||||
predicates.add(i % 3 == 0 ? -(i + 1) : i + 1);
|
|
||||||
}
|
}
|
||||||
long t0 = System.nanoTime();
|
long fixedNs = System.nanoTime() - startFixed;
|
||||||
mergeFilterUnpatched(predicates);
|
|
||||||
return System.nanoTime() - t0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long benchMergePatched(int p) {
|
double ratio = (double) defectiveNs / fixedNs;
|
||||||
List<Integer> predicates = new ArrayList<>();
|
|
||||||
for (int i = 0; i < p; i++) {
|
|
||||||
predicates.add(i % 3 == 0 ? -(i + 1) : i + 1);
|
|
||||||
}
|
|
||||||
long t0 = System.nanoTime();
|
|
||||||
mergeFilterPatched(predicates);
|
|
||||||
return System.nanoTime() - t0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long benchIntersectUnpatched(int g) {
|
System.out.printf("tidb-0001 updateDroppingPartitionInfo P=%d D=%d%n", P, D);
|
||||||
List<Integer> pg = new ArrayList<>();
|
System.out.printf(" defective: %,d ns%n", defectiveNs);
|
||||||
List<Integer> other = new ArrayList<>();
|
System.out.printf(" fixed: %,d ns%n", fixedNs);
|
||||||
for (int i = 0; i < g; i++) { pg.add(i); other.add(g - 1 - i); }
|
System.out.printf(" ratio: %.1fx%n", ratio);
|
||||||
long t0 = System.nanoTime();
|
|
||||||
intersectUnpatched(pg, other);
|
|
||||||
return System.nanoTime() - t0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long benchIntersectPatched(int g) {
|
assert ratio > 5.0
|
||||||
List<Integer> pg = new ArrayList<>();
|
: "Expected significant speedup, got only " + ratio + "x";
|
||||||
List<Integer> other = new ArrayList<>();
|
System.out.println("PASS performance (ratio=" + String.format("%.1f", ratio) + "x)");
|
||||||
for (int i = 0; i < g; i++) { pg.add(i); other.add(g - 1 - i); }
|
|
||||||
long t0 = System.nanoTime();
|
|
||||||
intersectPatched(pg, other);
|
|
||||||
return System.nanoTime() - t0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
// Main
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("=== tidb-0001: mergeInAndNotEQLists removeValues ===");
|
testCorrectness();
|
||||||
|
testPerformance();
|
||||||
// Correctness
|
System.out.println("ALL TESTS PASSED");
|
||||||
List<Integer> preds = Arrays.asList(-1, 2, -3, 4, -5, 6);
|
|
||||||
List<Integer> r1 = mergeFilterUnpatched(preds);
|
|
||||||
List<Integer> r2 = mergeFilterPatched(preds);
|
|
||||||
assertEquals(r1, r2, "tidb-0001 correctness (unpatched==patched output)");
|
|
||||||
|
|
||||||
// Warmup
|
|
||||||
for (int i = 0; i < 3; i++) { benchMergeUnpatched(200); benchMergePatched(200); }
|
|
||||||
|
|
||||||
// Benchmark P=500 predicates
|
|
||||||
int P = 500;
|
|
||||||
long u1 = 0, p1 = 0;
|
|
||||||
int rounds = 5;
|
|
||||||
for (int i = 0; i < rounds; i++) { u1 += benchMergeUnpatched(P); p1 += benchMergePatched(P); }
|
|
||||||
u1 /= rounds; p1 /= rounds;
|
|
||||||
double ratio1 = (double) u1 / Math.max(p1, 1);
|
|
||||||
System.out.printf(" P=%d unpatched=%,d ns patched=%,d ns ratio=%.1fx%n", P, u1, p1, ratio1);
|
|
||||||
if (ratio1 < 2.0) System.out.println(" WARN: ratio below 2x (small N may not show O(N²) effect)");
|
|
||||||
System.out.println("PASS tidb-0001 benchmark");
|
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("=== tidb-0002: ListPartitionGroup.intersect ===");
|
|
||||||
|
|
||||||
// Correctness
|
|
||||||
List<Integer> pg = Arrays.asList(0, 1, 2, 3, 4);
|
|
||||||
List<Integer> other = Arrays.asList(2, 3, 5, 6);
|
|
||||||
List<Integer> r3 = intersectUnpatched(pg, other);
|
|
||||||
List<Integer> r4 = intersectPatched(pg, other);
|
|
||||||
assertEquals(r3, r4, "tidb-0002 correctness (unpatched==patched output)");
|
|
||||||
|
|
||||||
// Warmup
|
|
||||||
for (int i = 0; i < 3; i++) { benchIntersectUnpatched(200); benchIntersectPatched(200); }
|
|
||||||
|
|
||||||
// Benchmark G=1000 group indices
|
|
||||||
int G = 1000;
|
|
||||||
long u2 = 0, p2 = 0;
|
|
||||||
for (int i = 0; i < rounds; i++) { u2 += benchIntersectUnpatched(G); p2 += benchIntersectPatched(G); }
|
|
||||||
u2 /= rounds; p2 /= rounds;
|
|
||||||
double ratio2 = (double) u2 / Math.max(p2, 1);
|
|
||||||
System.out.printf(" G=%d unpatched=%,d ns patched=%,d ns ratio=%.1fx%n", G, u2, p2, ratio2);
|
|
||||||
if (ratio2 < 2.0) System.out.println(" WARN: ratio below 2x");
|
|
||||||
System.out.println("PASS tidb-0002 benchmark");
|
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("ALL PASS");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue