# doctrine-orm-0003 — SqlWalker::walkObjectExpression: O(fields²) partial field set check per query **Status:** OPEN **Severity:** MEDIUM **Component:** `doctrine/orm` — `src/Doctrine/ORM/Query/SqlWalker.php` **Affects:** DQL queries using partial object selects (`SELECT PARTIAL p.{id,name}`) --- ## Root Cause `SqlWalker.php:1405` and `1445` — `walkObjectExpression()`: ```php // DEFECTIVE — O(partialFieldSet) per field, inside foreach fieldMappings foreach ($class->fieldMappings as $fieldName => $mapping) { if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true)) { continue; } // ... build SQL column list } // And for subclasses (line 1445): foreach ($subClass->fieldMappings as $fieldName => $mapping) { if (isset($mapping->inherited) || ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true))) { continue; } ``` `$partialFieldSet` is a plain PHP array of field name strings. For each field in `$class->fieldMappings` (F fields), `in_array` scans all P entries in `$partialFieldSet`. Total: O(F × P) per class, O(F × P × subclasses) for STI/CTI hierarchies. Called once per query compilation (not per result row), but DQL parsing without a query result cache repeats this on every request. --- ## Complexity Analysis | Metric | Defective | Fixed | |--------|-----------|-------| | Single field check | O(partialFieldSet size P) | O(1) | | Full entity (F fields) | O(F × P) | O(F) | | With S subclasses (STI) | O(F × P × S) | O(F × S) | --- ## Fix Convert `$partialFieldSet` array to a hash set before entering the loop: ```php // FIXED — build O(1)-lookup set once, before the loops $partialFieldIndex = $partialFieldSet ? array_flip($partialFieldSet) : null; foreach ($class->fieldMappings as $fieldName => $mapping) { if ($partialFieldIndex !== null && ! isset($partialFieldIndex[$fieldName])) { continue; } // ... } ``` Same fix applies at line 1445 for the subclass loop. --- ## Call Sites | File | Line | Context | |------|------|---------| | `src/Query/SqlWalker.php` | 1405 | `walkObjectExpression()` — primary class fields | | `src/Query/SqlWalker.php` | 1445 | `walkObjectExpression()` — STI/CTI subclass fields | --- ## Speedup Estimate Queries with large partial field sets (P > 10) or large entities (F > 20) with deep STI hierarchies (S > 10): O(P)× improvement in query compilation time on the partial-select field filter path.