icingaweb2/library/Icinga/Data/Db/Query.php

344 lines
8.9 KiB
PHP

<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Data\Db;
use Icinga\Filter\Query\Node;
use Icinga\Filter\Query\Tree;
use Zend_Db_Select;
use Icinga\Data\BaseQuery;
use Icinga\Application\Benchmark;
/**
* Db/Query class for implementing database queries
*/
class Query extends BaseQuery
{
/**
* Zend_Db_Adapter_Abstract
*
*
*/
protected $db;
/**
* Base Query will be prepared here, has tables and cols
* shared by full & count query
*/
protected $baseQuery;
/**
* Select object
*/
private $selectQuery;
/**
* Select object used for count query
*/
private $countQuery;
/**
* Allow to override COUNT(*)
*/
protected $countColumns;
protected $useSubqueryCount = false;
protected $countCache;
protected $maxCount;
protected function init()
{
$this->db = $this->ds->getConnection();
$this->baseQuery = $this->db->select();
}
public function __clone()
{
if ($this->baseQuery !== null) {
$this->baseQuery = clone $this->baseQuery;
}
if ($this->selectQuery !== null) {
$this->selectQuery = clone $this->selectQuery;
}
if ($this->countQuery !== null) {
$this->countQuery = clone $this->countQuery;
}
}
/**
* Return the raw base query
*
* Modifications on this requires a call to Query::refreshQueryObjects()
*
* @return Zend_Db_Select
*
*/
public function getRawBaseQuery()
{
return $this->baseQuery;
}
/**
* Recreate the select and count queries
*
* Required when external modifications are made in the baseQuery
*/
public function refreshQueryObjects()
{
$this->createQueryObjects();
}
/**
* Return the select query and initialize it if not done yet
*
* @return Zend_Db_Select
*/
public function getSelectQuery()
{
if ($this->selectQuery === null) {
$this->createQueryObjects();
}
if ($this->hasLimit()) {
$this->selectQuery->limit($this->getLimit(), $this->getOffset());
}
return $this->selectQuery;
}
/**
* Return the current count query and initialize it if not done yet
*
* @return Zend_Db_Select
*/
public function getCountQuery()
{
if ($this->countQuery === null) {
$this->createQueryObjects();
}
return $this->countQuery;
}
/**
* Create the Zend_Db select query for this query
*/
private function createSelectQuery()
{
$this->selectQuery = clone($this->baseQuery);
$this->selectQuery->columns($this->getColumns());
$this->selectQuery->distinct($this->isDistinct());
if ($this->hasOrder()) {
foreach ($this->getOrderColumns() as $col) {
$this->selectQuery->order(
$col[0] . ' ' . (($col[1] === self::SORT_DESC) ? 'DESC' : 'ASC')
);
}
}
}
/**
* Create a countquery by using the select query as a subselect and count it's result
*
* This is a rather naive approach and not suitable for complex queries or queries with many results
*
* @return Zend_Db_Select The query object representing the count
*/
private function createCountAsSubQuery()
{
$query = clone($this->selectQuery);
if ($this->maxCount === null) {
return $this->db->select()->from($query, 'COUNT(*)');
} else {
return $this->db->select()->from(
$query->reset('order')->limit($this->maxCount),
'COUNT(*)'
);
}
}
/**
* Create a custom count query based on the columns set in countColumns
*
* @return Zend_Db_Select The query object representing the count
*/
private function createCustomCountQuery()
{
$query = clone($this->baseQuery);
if ($this->countColumns === null) {
$this->countColumns = array('cnt' => 'COUNT(*)');
}
$query->columns($this->countColumns);
return $query;
}
/**
* Create a query using the selected operation
*
* @see Query::createCountAsSubQuery() Used when useSubqueryCount is true
* @see Query::createCustomCountQuery() Called when useSubqueryCount is false
*/
private function createCountQuery()
{
if ($this->isDistinct() || $this->useSubqueryCount) {
$this->countQuery = $this->createCountAsSubQuery();
} else {
$this->countQuery = $this->createCustomCountQuery();
}
}
protected function beforeQueryCreation()
{
}
protected function afterQueryCreation()
{
}
/**
* Create the Zend_Db select and count query objects for this instance
*/
private function createQueryObjects()
{
$this->beforeQueryCreation();
$this->applyFilter();
$this->createSelectQuery();
$this->createCountQuery();
$this->afterQueryCreation();
}
/**
* Query the database and fetch the result count of this query
*
* @return int The result count of this query as returned by the database
*/
public function count()
{
if ($this->countCache === null) {
Benchmark::measure('DB is counting');
$this->countCache = $this->db->fetchOne($this->getCountQuery());
Benchmark::measure('DB finished count');
}
return $this->countCache;
}
/**
* Query the database and return all results
*
* @return array An array containing subarrays with all results contained in the database
*/
public function fetchAll()
{
Benchmark::measure('DB is fetching All');
$result = $this->db->fetchAll($this->getSelectQuery());
Benchmark::measure('DB fetch done');
return $result;
}
/**
* Query the database and return the next result row
*
* @return array An array containing the next row of the database result
*/
public function fetchRow()
{
return $this->db->fetchRow($this->getSelectQuery());
}
/**
* Query the database and return a single column of the result
*
* @return array An array containing the first column of the result
*/
public function fetchColumn()
{
return $this->db->fetchCol($this->getSelectQuery());
}
/**
* Query the database and return a single result
*
* @return array An associative array containing the first result
*/
public function fetchOne()
{
return $this->db->fetchOne($this->getSelectQuery());
}
/**
* Query the database and return key=>value pairs using hte first two columns
*
* @return array An array containing key=>value pairs
*/
public function fetchPairs()
{
return $this->db->fetchPairs($this->getSelectQuery());
}
/**
* Return the select and count query as a textual representation
*
* @return string An 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 the select query
*
* The paginator expects this, so we can't use debug output here
*
* @return Zend_Db_Select
*/
public function __toString()
{
return strval($this->getSelectQuery());
}
public function applyFilter()
{
$parser = new TreeToSqlParser($this);
$parser->treeToSql($this->getFilter(), $this->baseQuery);
$this->clearFilter();
}
}