# xtuple-0001: orutils.cpp missingParamList QStringList::contains O(P*M) in SQL param parse loop **Project:** xTuple / OpenRPT (open-source ERP report engine) **File:** `OpenRPT/renderer/orutils.cpp` **MOAD:** 0001 (CWE-407 — Inefficient Algorithmic Complexity) **Severity:** LOW-MEDIUM **Speedup:** ~10-50x at P=1000, M=500 ## Description `orQuery::orQuery()` parses a SQL string to find parameter placeholders (`$"name"` and `%N` style). As it encounters each placeholder, it checks whether the parameter name is already in `missingParamList` before appending: ```cpp // OpenRPT/renderer/orutils.cpp:65 if(!missingParamList.contains(n)) missingParamList.append(n); ``` `missingParamList` is declared as `QStringList` (i.e. `QList`). `QStringList::contains()` is a linear O(M) scan where M is our current missing param count. The outer loop iterates P times (once per param placeholder in the SQL). Total complexity: O(P * M). For a parametric report template with hundreds of placeholder occurrences and many distinct missing params (e.g., a batch report runner that pre-scans many SQL templates), this compounds into measurable overhead. ## Fix Replace the membership check with a `QSet` shadow set for O(1) average-case lookup, while keeping `missingParamList` as the ordered `QStringList` for downstream consumption (it is a public member used by callers to display ordered missing-param dialogs). ```cpp QSet missingParamSet; // ... if(!missingParamSet.contains(n)) { missingParamSet.insert(n); missingParamList.append(n); } ``` ## Complexity | Version | Time | |---------|------| | Before | O(P * M) | | After | O(P) amortized | ## Affected File `OpenRPT/renderer/orutils.cpp` (and `orutils.h` for the shadow set field) ## Test `defects/xtuple-0001/test/test_xtuple_0001.py` — simulates the pattern in Python, benchmarks N=1000 param slots with M=500 distinct missing params, asserts speedup > 3x.