icingaweb2/library/Icinga/Backend/Ido/Query.php

522 lines
16 KiB
PHP
Executable File

<?php
namespace Icinga\Backend\Ido;
abstract class Query extends \Icinga\Backend\Query
{
protected $db;
protected $prefix;
protected $query;
protected $count_query;
protected $count_columns;
protected $ordered = false;
protected $finalized = false;
protected $object_id = 'object_id';
protected $hostgroup_id = 'hostgroup_id';
protected $servicegroup_id = 'servicegroup_id';
protected $custom_cols = array();
/**
* Available sort combinations
*/
protected $order_columns = array(
'host' => array(
'ASC' => array(
'host_name ASC',
'service_description ASC'
),
'DESC' => array(
'host_name DESC',
'service_description ASC'
),
'default' => 'ASC'
),
'host_address' => array(
'ASC' => array(
'host_ipv4 ASC',
'service_description ASC'
),
'DESC' => array(
'host_ipv4 ASC',
'service_description ASC'
),
'default' => 'ASC'
),
'service' => array(
'ASC' => array(
'service_description ASC'
),
'DESC' => array(
'service_description DESC'
),
'default' => 'ASC'
),
'service_state_change' => array(
'ASC' => array(
'ss.last_state_change ASC'
),
'DESC' => array(
'ss.last_state_change DESC'
),
'default' => 'DESC'
),
'service_state' => array(
'ASC' => array(
'CASE WHEN (ss.current_state = 3) THEN 2 WHEN (ss.current_state = 2) THEN 3 ELSE ss.current_state END DESC', // TODO: distinct severity in a better way
'ss.problem_has_been_acknowledged ASC',
// 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END ASC',
'service_in_downtime ASC', // TODO: Check if all dbs allow sorting by alias
'ss.last_state_change DESC',
'so.name1 ASC',
'so.name2 ASC'
),
'DESC' => array(
'CASE WHEN (ss.current_state = 3) THEN 2 WHEN (ss.current_state = 2) THEN 3 ELSE ss.current_state END ASC',
'ss.problem_has_been_acknowledged ASC',
// 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END ASC',
'service_in_downtime ASC',
'ss.last_state_change DESC'
),
'default' => 'ASC'
)
);
abstract protected function createQuery();
public function dump()
{
$this->finalize();
return "QUERY\n=====\n"
. $this->query
. "\n\nCOUNT\n=====\n"
. $this->count_query
. "\n\n";
}
public function getCountQueryObject()
{
return $this->finalize()->count_query;
}
public function getQueryObject()
{
return $this->finalize()->query;
}
protected function createCountQuery()
{
return clone($this->query);
}
protected function init()
{
$this->db = $this->backend->getAdapter();
$this->dbtype = $this->backend->getDbType();
if ($this->dbtype === 'oracle') {
$this->object_id = $this->hostgroup_id = $this->servicegroup_id = 'id';
}
$this->prefix = $this->backend->getPrefix();
$this->query = $this->createQuery();
$this->count_query = $this->createCountQuery();
}
protected function finalize()
{
if ($this->finalized) return $this;
$this->finalized = true;
$this->query->columns($this->columns);
if ($this->count_columns === null) {
$this->count_columns = array('cnt' => 'COUNT(*)');
}
if (! $this->ordered) {
$this->order();
}
$this->count_query->columns($this->count_columns);
return $this;
}
protected function prepareServiceStatesQuery()
{
$query = $this->db->select()
->from(
array('hs' => $this->prefix . 'hoststatus'),
array()
)->join(
array('h' => $this->prefix . 'hosts'),
'hs.host_object_id = h.host_object_id',
array()
)->join(
array('s' => $this->prefix . 'services'),
's.host_object_id = h.host_object_id',
array()
)->join(
array('so' => $this->prefix . 'objects'),
"so.$this->object_id = s.service_object_id AND so.is_active = 1",
array()
)->joinLeft(
array('ss' => $this->prefix . 'servicestatus'),
"so.$this->object_id = ss.service_object_id",
array()
);
// $this->joinServiceDowntimes($query);
// $query->group('so.object_id');
return $query;
}
protected function prepareServicesCount()
{
// TODO: Depends on filter, some cols could be avoided
$query = $this->db->select()->from(
array('hs' => $this->prefix . 'hoststatus'),
array()
)->join(
array('h' => $this->prefix . 'hosts'),
'hs.host_object_id = h.host_object_id',
array()
)->join(
array('s' => $this->prefix . 'services'),
's.host_object_id = h.host_object_id',
array()
)->join(
array('so' => $this->prefix . 'objects'),
"so.$this->object_id = s.service_object_id AND so.is_active = 1",
"COUNT(so.$this->object_id)"
)->joinLeft(
array('ss' => $this->prefix . 'servicestatus'),
"so.$this->object_id = ss.service_object_id",
array()
);
// $this->joinServiceDowntimes($query);
return $query;
}
protected function joinHostgroups($query = null)
{
if ($query === null) $query = $this->query;
$query->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = h.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
"hgm.hostgroup_id = hg.$this->hostgroup_id",
array()
);
return $this;
}
protected function joinServiceHostgroups($query)
{
if ($query === null) $query = $this->query;
$query->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = s.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
"hgm.hostgroup_id = hg.$this->hostgroup_id",
array()
);
return $this;
}
protected function joinServicegroups($query)
{
if ($query === null) $query = $this->query;
$query->join(
array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.service_object_id = s.service_object_id',
array()
)->join(
array('sg' => $this->prefix . 'servicegroups'),
"sgm.servicegroup_id = sg.$this->servicegroup_id",
array()
);
return $this;
}
protected function joinServiceDowntimes($query)
{
$query->joinLeft(
array('dt' => $this->prefix . 'scheduleddowntime'),
"so.$this->object_id = dt.object_id"
. ' AND dt.is_in_effect = 1',
array()
);
// NDO compat (doesn't work correctly like this):
// $now = "'" . date('Y-m-d H:i:s') . "'";
// . ' AND dt.was_started = 1'
// . ' AND dt.scheduled_end_time > ' . $now
// . ' AND dt.actual_start_time < ' . $now,
return $query;
}
public function where($column, $value = null)
{
// Ugly temporary hack:
foreach (array($this->query, $this->count_query) as $query) {
if ($column === 'search') {
if ($this->dbtype === 'mysql') {
$query->where($this->db->quoteInto(
'so.name2 COLLATE latin1_general_ci LIKE ?'
. ' OR so.name1 COLLATE latin1_general_ci LIKE ?',
'%' . $value . '%',
'%' . $value . '%'
));
} else {
$query->where($this->db->quoteInto(
'LOWER(so.name2) LIKE ?'
. ' OR LOWER(so.name1) LIKE ?',
'%' . strtolower($value) . '%',
'%' . strtolower($value) . '%'
));
}
continue;
}
// TODO: Check if this also works based on column:
if ($column === 'hostgroups') {
$this->appendHostgroupLimit($query, $value);
continue;
}
if (preg_match('~^_([^_]+)_(.+)$~', $column, $m)) {
switch($m[1]) {
case 'host':
$this->appendHostCustomVarLimit($query, $m[2], $value);
break;
case 'service':
$this->appendServiceCustomVarLimit($query, $m[2], $value);
break;
}
continue;
}
//$column = preg_replace('~^current_state~', 'ss.current_state', $column);
if (array_key_exists($column, $this->available_columns)) {
$column = $this->available_columns[$column];
}
$query->where($this->prepareFilterStringForColumn($column, $value));
}
/*->orWhere('last_state_change > ?', $new)*/
return $this;
}
public function order($column = '', $dir = null)
{
$this->ordered = true;
return $this->applyOrder($column, $dir);
}
protected function applyOrder($order = '', $order_dir = null)
{
if (! array_key_exists($order, $this->order_columns)) {
$order = key($this->order_columns);
}
if ($order_dir === null) {
$order_dir = $this->order_columns[$order]['default'];
}
foreach ($this->order_columns[$order][$order_dir] as $col) {
$this->query->order($col);
}
return $this;
}
protected function addServiceComments($query = null)
{
if ($query === null) {
$query = $this->query;
}
$query->joinLeft(
array('co' => $this->prefix . 'comments'),
"so.$this->object_id = co.object_id",
array()
)
->group('so.object_id')
;
return $this;
}
/**
* $column = col
* $value = abc,cde,cd*,!egh,!*hh*
* -> (col IN ('abc', 'cde') OR col LIKE 'cd%') AND (col != 'egh' AND col NOT LIKE '%hh%')
*/
protected function prepareFilterStringForColumn($column, $value)
{
$filter = '';
$filters = array();
$or = array();
$and = array();
if (strpos($value, ',') !== false) {
$value = preg_split('~,~', $value, -1, PREG_SPLIT_NO_EMPTY);
}
if (! is_array($value)) {
$value = array($value);
}
// Go through all given values
foreach ($value as $val) {
// Value starting with - means negation
if ($val[0] === '-') {
$val = substr($val, 1);
if (strpos($val, '*') === false) {
$and[] = $this->db->quoteInto($column . ' != ?', $val);
} else {
$and[] = $this->db->quoteInto(
$column . ' NOT LIKE ?',
str_replace('*', '%', $val)
);
}
// Starting with + enforces AND
} elseif ($val[0] === '+') {
$val = substr($val, 1);
if (strpos($val, '*') === false) {
$and[] = $this->db->quoteInto($column . ' = ?', $val);
} else {
$and[] = $this->db->quoteInto(
$column . ' LIKE ?',
str_replace('*', '%', $val)
);
}
// All others ar ORs:
} else {
if (strpos($val, '*') === false) {
$or[] = $this->db->quoteInto($column . ' = ?', $val);
} else {
$or[] = $this->db->quoteInto(
$column . ' LIKE ?',
str_replace('*', '%', $val)
);
}
}
}
if (! empty($or)) { $filters[] = implode(' OR ', $or); }
if (! empty($and)) { $filters[] = implode(' AND ', $and); }
if (! empty($filters)) {
$filter = '(' . implode(') AND (', $filters) . ')';
}
return $filter;
}
protected function addCustomVarColumn($query, $alias, $name, $filter = null)
{
// TODO: Improve this:
if (! preg_match('~^[a-zA-Z0-9_]+$~', $name)) {
throw new \Exception(sprintf(
'Got invalid custom var: "%s"',
$name
));
}
$qobj = spl_object_hash($query);
if (! array_key_exists($qobj, $this->custom_cols)) {
$this->custom_cols[$qobj] = array();
}
if (array_key_exists($alias, $this->custom_cols[$qobj])) {
if ($name !== $this->custom_cols[$qobj][$alias]) {
throw new \Exception(sprintf(
'Cannot add CV alias "%s" twice with different target',
$alias
));
}
return $this;
}
$query->join(
// TODO: Allow multiple limits with different aliases
array($alias => $this->prefix . 'customvariablestatus'),
's.host_object_id = ' . $alias . '.object_id'
. ' AND ' . $alias . '.varname = '
. $this->db->quote(strtoupper($name))
//. ($filter === null ? '' : ' AND ' . $filter),
,
array()
);
$this->custom_cols[$qobj][$alias] = $name;
return $this;
}
protected function appendHostCustomVarLimit($query, $key, $value)
{
$alias = 'hcv_' . strtolower($key);
$filter = $this->prepareFilterStringForColumn($alias . '.varvalue', $value);
$this->addCustomVarColumn($query, $alias, $key);
$query->where($filter);
return $query;
}
protected function appendHostgroupLimit($query, $hostgroups)
{
return $query->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = s.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
"hgm.hostgroup_id = hg.$this->hostgroup_id",
array()
)
->where('hg.alias IN (?)', $hostgroups);
}
public function count()
{
return $this->db->fetchOne(
$this->finalize()->count_query
);
}
public function fetchAll()
{
return $this->db->fetchAll($this->finalize()->query);
}
public function fetchRow()
{
return $this->db->fetchRow($this->finalize()->query);
}
public function fetchOne()
{
return $this->db->fetchOne($this->finalize()->query);
}
public function fetchPairs()
{
return $this->db->fetchPairs($this->finalize()->query);
}
/**
* Sets a limit count and offset to the query
*
* @param int $count Number of rows to return
* @param int $offset Row offset to start from
* @return \Icinga\Backend\Query This Query object
*/
public function limit($count = null, $offset = null)
{
$this->query->limit($count, $offset);
return $this;
}
public function __toString()
{
$this->finalize();
return (string) $this->query;
}
}