Problem
Started as an issue to allow query like SELECT ... FROM ... WHERE ... AND (SELECT COUNT(*) ...) = value)
, it turns out that the new algorithm is very generic and allows for many more situations that are allowed by (standard) SQL:
- Select query at left hand side:
SELECT ...
FROM ...
WHERE (SELECT COUNT(*) FROM ... WHERE ...) > value
- Select query at right hand side:
SELECT ...
FROM ...
WHERE value < (SELECT AVG(...) FROM ...)
- Multiple select queries at right hand side:
SELECT ...
FROM ...
WHERE value BETWEEN (SELECT MIN(...) FROM ...) AND (SELECT MAX(...) FROM ...)
- Select query at left and right hand side (new test):
SELECT t.name
FROM test t
WHERE (SELECT AVG(tt.priority) FROM test_task tt WHERE tt.pid = t.id) > (SELECT AVG(tt2.priority) FROM test_task tt2)
- Select query at left hand side and multiple select queries at right hand side:
SELECT ...
FROM ...
WHERE (SELECT AVG(...) FROM ... WHERE ...) BETWEEN (SELECT MIN(...) FROM ...) AND (SELECT MAX(...) FROM ...)
Original post
[Note: issue #369314: Subselects don't work in DB conditions... handled only the IN case]
I was trying to construct a query with a subselect that looks like:
SELECT ...
FROM ...
WHERE ...
AND (SELECT COUNT(*) ...) = value)
However, I did not entirely manage to get it to work.
Attempt 1:
$query->condition($count_subquery, $value, '=');
Fails because $count_subquery is a SelectQueryInterface which is a QueryConditionInterface. And if the $field argument is a QueryConditionInterface it gets compiled and placed in the main query as is, thereby ignoring the $value and $operator arguments.
Attempt 2:
$query->condition($value, $count_subquery, '=');
Fails because no brackets are placed around the subquery.
This leads to a workaround:
$query->condition($value, $count_subquery, 'IN');
This happens to work because the IN operator places brackets around the value(s). However, it will not always be possible to use IN. There are times you may want to use a comparison operator other than '=', e.g. <, <=, >, >=. Also, at times, it may be necessary to compare 2 subselects (select objects whose average score is higher than the overall average score).
So, the error can be split into 2 situations:
1) SelectQueryInterface objects as the left hand side ($field operator) are not handled correctly.
2) SelectQueryInterface objects as the right hand side ($value operator) are only handled correctly when the operator happens to be 'IN'.
Proposed resolution
To solve 1)
Method DatabaseCondition::compile(): Add an additional check to this if:
if ($condition['field'] instanceof QueryConditionInterface) {
Something like:
if ($condition['field'] instanceof QueryConditionInterface && !($condition['field'] instanceof SelectQueryInterface && isset($condition['value']))) {
Subsequently the else branch needs to handle this situation, by adding:
- Compile on the $condition['field'] if it is a SelectQueryInterface.
- Brackets around that result of compile.
To solve 2)
- This will need some refactoring of the above mentioned else branch.
Remaining tasks
Can some experts regarding this part of Drupal confirm this is indeed an error and is a use case that is to be supported. Is the proposed solution in the correct direction. If so, I will try to write a patch including tests.