Thomas Gelf db3accc704 Data\Db: rename Query and Connection to Db...
Class names in namespaces should not be chosen as once we didn't have
such. The fact that we already did "use Db\Connection as DbConnection"
is the best hint that naming was wrong.

So this patch renames Db\Connection to Db\DbConnection and does the
same with DbQuery. DbQuery has been adjusted to fit our new SimpleQuery
and to handle the new Filter implementation.
2014-06-06 06:43:13 +00:00

239 lines
6.1 KiB
PHP

<?php
namespace Icinga\Data\Db;
use Icinga\Data\SimpleQuery;
use Icinga\Application\Benchmark;
use Icinga\Data\Filter\FilterOperator;
use Icinga\Data\Filter\FilterOr;
use Icinga\Data\Filter\FilterAnd;
use Icinga\Data\Filter\FilterNot;
use Zend_Db_Select;
/**
* Database query class
*/
class DbQuery extends SimpleQuery
{
/**
* @var Zend_Db_Adapter_Abstract
*/
protected $db;
/**
* Select query
*
* @var Zend_Db_Select
*/
protected $select;
/**
* Whether to use a subquery for counting
*
* When the query is distinct or has a HAVING or GROUP BY clause this must be set to true
*
* @var bool
*/
protected $useSubqueryCount = false;
/**
* Set the count maximum
*
* If the count maximum is set, count queries will not count more than that many rows. You should set this
* property only for really heavy queries.
*
* @var int
*/
protected $maxCount;
/**
* Count query result
*
* Count queries are only executed once
*
* @var int
*/
protected $count;
protected function init()
{
$this->db = $this->ds->getDbAdapter();
parent::init();
}
public function where($condition, $value = null)
{
// $this->count = $this->select = null;
return parent::where($condition, $value);
}
protected function dbSelect()
{
if ($this->select === null) {
$this->select = $this->db->select()->from($this->table, array());
}
return clone $this->select;
}
/**
* Get the select query
*
* Applies order and limit if any
*
* @return Zend_Db_Select
*/
public function getSelectQuery()
{
$select = $this->dbSelect();
$select->columns($this->columns);
$this->applyFilterSql($select);
if ($this->hasLimit() || $this->hasOffset()) {
$select->limit($this->getLimit(), $this->getOffset());
}
if ($this->hasOrder()) {
foreach ($this->getOrder() as $fieldAndDirection) {
$select->order(
$fieldAndDirection[0] . ' ' . $fieldAndDirection[1]
);
}
}
return $select;
}
protected function applyFilterSql($query)
{
$where = $this->renderFilter($this->filter);
if ($where !== '') {
$query->where($where);
}
}
protected function renderFilter($filter, $level = 0)
{
$str = '';
if ($filter instanceof FilterOperator) {
if ($filter instanceof FilterAnd) {
$op = ' AND ';
} elseif ($filter instanceof FilterOr) {
$op = ' OR ';
} elseif ($filter instanceof FilterNot) {
$op = ' AND ';
$str .= ' NOT ';
} else {
throw new \Exception('Cannot render filter: ' . $filter);
}
$parts = array();
if (! $filter->isEmpty()) {
foreach ($filter->filters() as $f) {
$parts[] = $this->renderFilter($f, $level + 1);
}
if ($level > 0) {
$str .= ' (' . implode($op, $parts) . ') ';
} else {
$str .= implode($op, $parts);
}
}
} else {
// TODO: render Filter (Where/like/time...)
$str .= $this->whereToSql($filter->getColumn(), $filter->getExpression());
}
return $str;
}
protected function escapeForSql($value)
{
// bindParam? bindValue?
if (is_array($value)) {
$ret = array();
foreach ($value as $val) {
$ret[] = $this->escapeForSql($val);
}
return implode(', ', $ret);
} else {
//if (preg_match('/^\d+$/', $value)) {
// return $value;
//} else {
return $this->db->quote($value);
//}
}
}
protected function escapeWildcards($value)
{
return preg_replace('/\*/', '%', $value);
}
public function whereToSql($col, $expression)
{
if (is_array($expression)) {
// TODO: Should we support this? Doesn't work for blub*
return $col . ' IN (' . $this->escapeForSql($expression) . ')';
} elseif (strpos($expression, '*') === false) {
return $col . ' = ' . $this->escapeForSql($expression);
} else {
return $col . ' LIKE ' . $this->escapeForSql($this->escapeWildcards($expression));
}
}
/**
* Get the count query
*
* @return Zend_Db_Select
*/
public function getCountQuery()
{
// TODO: there may be situations where we should clone the "select"
$count = $this->dbSelect();
$this->applyFilterSql($count);
if ($this->useSubqueryCount) {
$columns = array('cnt' => 'COUNT(*)');
return $this->db->select()->from($count, $columns);
}
if ($this->maxCount !== null) {
return $this->db->select()->from($count->limit($this->maxCount));
}
$count->columns(array('cnt' => 'COUNT(*)'));
return $count;
}
/**
* Count all rows of the result set
*
* @return int
*/
public function count()
{
if ($this->count === null) {
Benchmark::measure('DB is counting');
$this->count = $this->db->fetchOne($this->getCountQuery());
Benchmark::measure('DB finished count');
}
return $this->count;
}
/**
* Return the select and count query as a textual representation
*
* @return string A string containing the select and count query, using unix style newlines as linebreaks
*/
public function dump()
{
return "QUERY\n=====\n"
. $this->getSelectQuery()
. "\n\nCOUNT\n=====\n"
. $this->getCountQuery()
. "\n\n";
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->getSelectQuery();
}
}