ObjectQuery, FilterRenderer: two new classes...
...required when working with the cache refs #13068
This commit is contained in:
parent
251eb4f9a5
commit
ec0cbac657
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Data\Db;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Data\Filter\FilterChain;
|
||||
use Icinga\Data\Filter\FilterException;
|
||||
use Icinga\Data\Filter\FilterExpression;
|
||||
|
||||
class IcingaObjectFilterRenderer
|
||||
{
|
||||
/** @var Filter */
|
||||
protected $filter;
|
||||
|
||||
/** @var IcingaObjectQuery */
|
||||
protected $query;
|
||||
|
||||
protected $columnMap = array(
|
||||
'host.name' => 'host.object_name',
|
||||
'service.name' => 'service.object_name',
|
||||
);
|
||||
|
||||
public function __construct(Filter $filter, IcingaObjectQuery $query)
|
||||
{
|
||||
$this->filter = clone($filter);
|
||||
$this->fixFilterColumns($this->filter);
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Filter $filter
|
||||
* @param IcingaObjectQuery $query
|
||||
*
|
||||
* @return IcingaObjectQuery
|
||||
*/
|
||||
public static function apply(Filter $filter, IcingaObjectQuery $query)
|
||||
{
|
||||
$self = new static($filter, $query);
|
||||
return $self->applyFilterToQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IcingaObjectQuery
|
||||
*/
|
||||
protected function applyFilterToQuery()
|
||||
{
|
||||
$this->query->escapedWhere($this->renderFilter($this->filter));
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Filter $filter
|
||||
*/
|
||||
protected function renderFilter(Filter $filter)
|
||||
{
|
||||
if ($filter->isChain()) {
|
||||
/** @var FilterChain $filter */
|
||||
return $this->renderFilterChain($filter);
|
||||
} else {
|
||||
/** @var FilterExpression $filter */
|
||||
return $this->renderFilterExpression($filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterChain $filter
|
||||
*
|
||||
* @throws FilterException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function renderFilterChain(FilterChain $filter)
|
||||
{
|
||||
$parts = array();
|
||||
foreach ($filter->filters() as $sub) {
|
||||
$parts[] = $this->renderFilter($sub);
|
||||
}
|
||||
|
||||
$op = $filter->getOperatorName();
|
||||
if ($op === 'NOT') {
|
||||
if (count($parts) !== 1) {
|
||||
throw new FilterException(
|
||||
'NOT should have exactly one child, got %s',
|
||||
count($parts)
|
||||
);
|
||||
}
|
||||
|
||||
return $op . ' ' . $parts[0];
|
||||
} else {
|
||||
if ($filter->isRootNode()) {
|
||||
return implode(' ' . $op . ' ', $parts);
|
||||
} else {
|
||||
return '(' . implode(' ' . $op . ' ', $parts) . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function fixFilterColumns(Filter $filter)
|
||||
{
|
||||
if ($filter->isExpression()) {
|
||||
/** @var FilterExpression $filter */
|
||||
$col = $filter->getColumn();
|
||||
if (array_key_exists($col, $this->columnMap)) {
|
||||
$filter->setColumn($this->columnMap[$col]);
|
||||
}
|
||||
if (strpos($col, 'vars.') === false) {
|
||||
$filter->setExpression(json_decode($filter->getExpression()));
|
||||
}
|
||||
} else {
|
||||
/** @var FilterChain $filter */
|
||||
foreach ($filter->filters() as $sub) {
|
||||
$this->fixFilterColumns($sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterExpression $filter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function renderFilterExpression(FilterExpression $filter)
|
||||
{
|
||||
$query = $this->query;
|
||||
$column = $query->getAliasforRequiredFilterColumn($filter->getColumn());
|
||||
return $query->whereToSql(
|
||||
$column,
|
||||
$filter->getSign(),
|
||||
$filter->getExpression()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Data\Db;
|
||||
|
||||
use Icinga\Data\Db\DbQuery;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Director\Db;
|
||||
use Zend_Db_Expr as ZfDbExpr;
|
||||
use Zend_Db_Select as ZfDbSelect;
|
||||
|
||||
class IcingaObjectQuery
|
||||
{
|
||||
const BASE_ALIAS = 'o';
|
||||
|
||||
/** @var Db */
|
||||
protected $connection;
|
||||
|
||||
/** @var string */
|
||||
protected $type;
|
||||
|
||||
/** @var \Zend_Db_Adapter_Abstract */
|
||||
protected $db;
|
||||
|
||||
/** @var ZfDbSelect */
|
||||
protected $query;
|
||||
|
||||
/** @var bool */
|
||||
protected $resolved;
|
||||
|
||||
/** @var array joined tables, alias => table */
|
||||
protected $requiredTables;
|
||||
|
||||
/** @var array maps table aliases, alias => table*/
|
||||
protected $aliases;
|
||||
|
||||
/** @var DbQuery */
|
||||
protected $dummyQuery;
|
||||
|
||||
/** @var array varname => alias */
|
||||
protected $joinedVars = array();
|
||||
|
||||
protected $customVarTable;
|
||||
|
||||
protected $baseQuery;
|
||||
|
||||
/**
|
||||
* IcingaObjectQuery constructor.
|
||||
*
|
||||
* @param string $type
|
||||
* @param Db $connection
|
||||
* @param bool $resolved
|
||||
*/
|
||||
public function __construct($type, Db $connection, $resolved = true)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->connection = $connection;
|
||||
$this->db = $connection->getDbAdapter();
|
||||
$this->resolved = $resolved;
|
||||
$baseTable = 'icinga_' . $type;
|
||||
$this->baseQuery = $this->db->select()
|
||||
->from(
|
||||
array(self::BASE_ALIAS => $baseTable),
|
||||
array('name' => 'object_name')
|
||||
)->order(self::BASE_ALIAS . '.object_name');
|
||||
}
|
||||
|
||||
public function joinVar($name)
|
||||
{
|
||||
if (! $this->hasJoinedVar($name)) {
|
||||
$type = $this->type;
|
||||
$alias = $this->safeVarAlias($name);
|
||||
$varAlias = $alias . '_v';
|
||||
// TODO: optionally $varRelation = sprintf('icinga_%s_resolved_var', $type);
|
||||
$varRelation = sprintf('icinga_%s_var', $type);
|
||||
$idCol = sprintf('%s.%s_id', $alias, $type);
|
||||
|
||||
$joinOn = sprintf('%s = %s.id', $idCol, self::BASE_ALIAS);
|
||||
$joinVarOn = $this->db->quoteInto(
|
||||
sprintf('%s.checksum = %s.checksum AND %s.varname = ?', $alias, $varAlias, $alias),
|
||||
$name
|
||||
);
|
||||
|
||||
$this->baseQuery->join(
|
||||
array($alias => $varRelation),
|
||||
$joinOn,
|
||||
array()
|
||||
)->join(
|
||||
array($varAlias => 'icinga_var'),
|
||||
$joinVarOn,
|
||||
array($alias => $varAlias . '.varvalue')
|
||||
);
|
||||
|
||||
$this->joinedVars[$name] = $varAlias . '.varvalue';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function list()
|
||||
{
|
||||
return $this->db->fetchCol(
|
||||
$this->baseQuery
|
||||
);
|
||||
}
|
||||
|
||||
protected function hasJoinedVar($name)
|
||||
{
|
||||
return array_key_exists($name, $this->joinedVars);
|
||||
}
|
||||
|
||||
public function getJoinedVarAlias($name)
|
||||
{
|
||||
return $this->joinedVars[$name];
|
||||
}
|
||||
|
||||
// TODO: recheck this
|
||||
protected function safeVarAlias($name)
|
||||
{
|
||||
$alias = preg_replace('/[^a-zA-Z0-9_]/', '', (string) $name);
|
||||
$cnt = 1;
|
||||
$checkAlias = $alias;
|
||||
while (in_array($checkAlias, $this->joinedVars)) {
|
||||
$cnt++;
|
||||
$checkAlias = $alias . '_' . $cnt;
|
||||
}
|
||||
|
||||
return $checkAlias;
|
||||
}
|
||||
|
||||
public function escapedWhere($where)
|
||||
{
|
||||
$this->baseQuery->where(new ZfDbExpr($where));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $column
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAliasforRequiredFilterColumn($column)
|
||||
{
|
||||
$dot = strpos($column, '.');
|
||||
list($key, $sub) = $this->splitFilterKey($column);
|
||||
if ($sub === null) {
|
||||
return $key;
|
||||
} else {
|
||||
$objectType = $key;
|
||||
}
|
||||
|
||||
if ($objectType === $this->type) {
|
||||
list($key, $sub) = $this->splitFilterKey($sub);
|
||||
if ($sub === null) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
if ($key === 'vars') {
|
||||
return $this->joinVar($sub)->getJoinedVarAlias($sub);
|
||||
} else {
|
||||
throw new NotFoundError('Not yet, my type: %s - %s', $objectType, $key);
|
||||
}
|
||||
} else {
|
||||
throw new NotImplementedError('Not yet: %s - %s', $objectType, $sub);
|
||||
}
|
||||
}
|
||||
|
||||
protected function splitFilterKey($key)
|
||||
{
|
||||
$dot = strpos($key, '.');
|
||||
if ($dot === false) {
|
||||
return array($key, null);
|
||||
} else {
|
||||
return array(substr($key, 0, $dot), substr($key, $dot + 1));
|
||||
}
|
||||
}
|
||||
|
||||
protected function requireTable($name)
|
||||
{
|
||||
if ($alias = $this->getTableAliasFromQuery($name)) {
|
||||
return $alias;
|
||||
}
|
||||
|
||||
$this->joinTable($name);
|
||||
}
|
||||
|
||||
protected function joinTable($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->requiredTables)) {
|
||||
$alias = $this->makeAlias($name);
|
||||
}
|
||||
|
||||
return $this->tableAliases($name);
|
||||
}
|
||||
|
||||
protected function hasAlias($name)
|
||||
{
|
||||
return array_key_exists($name, $this->aliases);
|
||||
}
|
||||
|
||||
protected function makeAlias($name)
|
||||
{
|
||||
if (substr($name, 0, 7) === 'icinga_') {
|
||||
$shortName = substr($name, 7);
|
||||
} else {
|
||||
$shortName = $name;
|
||||
}
|
||||
|
||||
$parts = preg_split('/_/', $shortName, -1);
|
||||
$alias = '';
|
||||
foreach ($parts as $part) {
|
||||
$alias .= $part[0];
|
||||
if (! $this->hasAlias($alias)) {
|
||||
return $alias;
|
||||
}
|
||||
}
|
||||
|
||||
$cnt = 1;
|
||||
{
|
||||
$cnt++;
|
||||
if (! $this->hasAlias($alias . $cnt)) {
|
||||
return $alias . $cnt;
|
||||
}
|
||||
|
||||
} while (! $this->hasAlias($alias));
|
||||
|
||||
return $alias;
|
||||
}
|
||||
|
||||
protected function getTableAliasFromQuery($table)
|
||||
{
|
||||
$tables = $this->query->getPart('from');
|
||||
$key = array_search($table, $tables);
|
||||
if ($key === null || $key === false) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
'joinType' => $type,
|
||||
'schema' => $schema,
|
||||
'tableName' => $tableName,
|
||||
'joinCondition' => $cond
|
||||
*/
|
||||
return $key;
|
||||
}
|
||||
|
||||
public function whereToSql($col, $sign, $expression)
|
||||
{
|
||||
return $this->dummyQuery()->whereToSql($col, $sign, $expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DbQuery
|
||||
*/
|
||||
protected function dummyQuery()
|
||||
{
|
||||
if ($this->dummyQuery === null) {
|
||||
$this->dummyQuery = $this->connection->select();
|
||||
}
|
||||
|
||||
return $this->dummyQuery;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue