Skip to content

Commit e168659

Browse files
committed
Transform custom variable filters as late as possible
fixes #865
1 parent 274b903 commit e168659

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

library/Icingadb/Model/Behavior/FlattenedObjectVars.php

+34-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use ipl\Orm\Contract\QueryAwareBehavior;
1212
use ipl\Orm\Contract\RewriteColumnBehavior;
1313
use ipl\Orm\Query;
14+
use ipl\Stdlib\Data;
1415
use ipl\Stdlib\Filter;
1516

1617
class FlattenedObjectVars implements RewriteColumnBehavior, QueryAwareBehavior
@@ -32,11 +33,39 @@ public function rewriteCondition(Filter\Condition $condition, $relation = null)
3233
$column = $condition->metaData()->get('columnName');
3334
if ($column !== null) {
3435
$relation = substr($relation, 0, -5) . 'customvar_flat.';
35-
$nameFilter = Filter::like($relation . 'flatname', $column);
36-
$class = get_class($condition);
37-
$valueFilter = new $class($relation . 'flatvalue', $condition->getValue());
38-
39-
return Filter::all($nameFilter, $valueFilter);
36+
$condition->metaData()
37+
->set('requiresTransformation', true)
38+
->set('columnPath', $relation . $column)
39+
->set('relationPath', substr($relation, 0, -1));
40+
41+
// The ORM's FilterProcessor only optimizes filter conditions that are in the same level (chain).
42+
// Previously, this behavior transformed a single condition to an ALL chain and hence the semantics
43+
// of the level changed, since the FilterProcessor interpreted the conditions separately from there on.
44+
// To not change the semantics of the condition it is required to delay the transformation of the condition
45+
// until the subquery is created. Though, since the FilterProcessor only applies behaviors once, this
46+
// hack is required. (The entire filter, metadata and optimization is total garbage.)
47+
$oldMetaData = $condition->metaData();
48+
$reflection = new \ReflectionClass($condition);
49+
$reflection->getProperty('metaData')->setAccessible(true);
50+
$reflection->getProperty('metaData')->setValue($condition, new class () extends Data {
51+
public function set($name, $value)
52+
{
53+
if ($name === 'behaviorsApplied') {
54+
return $this;
55+
}
56+
57+
return parent::set($name, $value);
58+
}
59+
});
60+
$condition->metaData()->merge($oldMetaData);
61+
62+
// But to make it even worse: If we do that, (not transforming the condition) the FilterProcessor sees
63+
// multiple conditions as targeting different columns, as it doesn't know that the *columns* are in fact
64+
// custom variables. It then attempts to combine the conditions with an AND, which is not possible, since
65+
// they refer to the same columns (flatname and flatvalue) after being transformed. So we have to make
66+
// the condition refer to a different column, which is totally irrelevant, but since it's always the same
67+
// column, the FilterProcessor won't attempt to combine the conditions. The literal icing on the cake.
68+
$condition->setColumn('always_the_same_but_totally_irrelevant');
4069
}
4170
}
4271

library/Icingadb/Model/CustomvarFlat.php

+15
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66

77
use ipl\Orm\Behavior\Binary;
88
use ipl\Orm\Behaviors;
9+
use ipl\Orm\Contract\RewriteFilterBehavior;
910
use ipl\Orm\Model;
1011
use ipl\Orm\Query;
1112
use ipl\Orm\Relations;
13+
use ipl\Stdlib\Filter;
14+
use ipl\Stdlib\Filter\Condition;
1215
use Traversable;
1316

1417
class CustomvarFlat extends Model
@@ -42,6 +45,18 @@ public function createBehaviors(Behaviors $behaviors)
4245
'customvar_id',
4346
'flatname_checksum'
4447
]));
48+
$behaviors->add(new class implements RewriteFilterBehavior {
49+
public function rewriteCondition(Condition $condition, $relation = null)
50+
{
51+
if ($condition->metaData()->has('requiresTransformation')) {
52+
$nameFilter = Filter::like($relation . 'flatname', $condition->metaData()->get('columnName'));
53+
$class = get_class($condition);
54+
$valueFilter = new $class($relation . 'flatvalue', $condition->getValue());
55+
56+
return Filter::all($nameFilter, $valueFilter);
57+
}
58+
}
59+
});
4560
}
4661

4762
public function createRelations(Relations $relations)

0 commit comments

Comments
 (0)