3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Db\Sql
;
12 use Zend\Db\Adapter\AdapterInterface
;
13 use Zend\Db\Adapter\Driver\DriverInterface
;
14 use Zend\Db\Adapter\StatementContainerInterface
;
15 use Zend\Db\Adapter\ParameterContainer
;
16 use Zend\Db\Adapter\Platform\PlatformInterface
;
17 use Zend\Db\Adapter\Platform\Sql92
as AdapterSql92Platform
;
21 * @property Where $where
22 * @property Having $having
24 class Select
extends AbstractSql
implements SqlInterface
, PreparableSqlInterface
30 const SELECT
= 'select';
31 const QUANTIFIER
= 'quantifier';
32 const COLUMNS
= 'columns';
33 const TABLE
= 'table';
34 const JOINS
= 'joins';
35 const WHERE
= 'where';
36 const GROUP
= 'group';
37 const HAVING
= 'having';
38 const ORDER
= 'order';
39 const LIMIT
= 'limit';
40 const OFFSET
= 'offset';
41 const QUANTIFIER_DISTINCT
= 'DISTINCT';
42 const QUANTIFIER_ALL
= 'ALL';
43 const JOIN_INNER
= 'inner';
44 const JOIN_OUTER
= 'outer';
45 const JOIN_LEFT
= 'left';
46 const JOIN_RIGHT
= 'right';
48 const ORDER_ASCENDING
= 'ASC';
49 const ORDER_DESCENDING
= 'DESC';
50 const COMBINE
= 'combine';
51 const COMBINE_UNION
= 'union';
52 const COMBINE_EXCEPT
= 'except';
53 const COMBINE_INTERSECT
= 'intersect';
57 * @var array Specifications
59 protected $specifications = array(
60 'statementStart' => '%1$s',
61 self
::SELECT
=> array(
62 'SELECT %1$s FROM %2$s' => array(
63 array(1 => '%1$s', 2 => '%1$s AS %2$s', 'combinedby' => ', '),
66 'SELECT %1$s %2$s FROM %3$s' => array(
68 array(1 => '%1$s', 2 => '%1$s AS %2$s', 'combinedby' => ', '),
74 array(3 => '%1$s JOIN %2$s ON %3$s', 'combinedby' => ' ')
77 self
::WHERE
=> 'WHERE %1$s',
79 'GROUP BY %1$s' => array(
80 array(1 => '%1$s', 'combinedby' => ', ')
83 self
::HAVING
=> 'HAVING %1$s',
85 'ORDER BY %1$s' => array(
86 array(1 => '%1$s', 2 => '%1$s %2$s', 'combinedby' => ', ')
89 self
::LIMIT
=> 'LIMIT %1$s',
90 self
::OFFSET
=> 'OFFSET %1$s',
91 'statementEnd' => '%1$s',
92 self
::COMBINE
=> '%1$s ( %2$s )',
98 protected $tableReadOnly = false;
103 protected $prefixColumnsWithTable = true;
106 * @var string|array|TableIdentifier
108 protected $table = null;
111 * @var null|string|Expression
113 protected $quantifier = null;
118 protected $columns = array(self
::SQL_STAR
);
123 protected $joins = array();
128 protected $where = null;
133 protected $order = array();
138 protected $group = null;
141 * @var null|string|array
143 protected $having = null;
148 protected $limit = null;
153 protected $offset = null;
158 protected $combine = array();
163 * @param null|string|array|TableIdentifier $table
165 public function __construct($table = null)
169 $this->tableReadOnly
= true;
172 $this->where
= new Where
;
173 $this->having
= new Having
;
179 * @param string|array|TableIdentifier $table
180 * @throws Exception\InvalidArgumentException
183 public function from($table)
185 if ($this->tableReadOnly
) {
186 throw new Exception\
InvalidArgumentException('Since this object was created with a table and/or schema in the constructor, it is read only.');
189 if (!is_string($table) && !is_array($table) && !$table instanceof TableIdentifier
) {
190 throw new Exception\
InvalidArgumentException('$table must be a string, array, or an instance of TableIdentifier');
193 if (is_array($table) && (!is_string(key($table)) ||
count($table) !== 1)) {
194 throw new Exception\
InvalidArgumentException('from() expects $table as an array is a single element associative array');
197 $this->table
= $table;
202 * @param string|Expression $quantifier DISTINCT|ALL
205 public function quantifier($quantifier)
207 if (!is_string($quantifier) && !$quantifier instanceof Expression
) {
208 throw new Exception\
InvalidArgumentException(
209 'Quantifier must be one of DISTINCT, ALL, or some platform specific Expression object'
212 $this->quantifier
= $quantifier;
217 * Specify columns from which to select
219 * Possible valid states:
224 * value can be strings or Expression objects
226 * array(string => value, ...)
227 * key string will be use as alias,
228 * value can be string or Expression objects
230 * @param array $columns
231 * @param bool $prefixColumnsWithTable
234 public function columns(array $columns, $prefixColumnsWithTable = true)
236 $this->columns
= $columns;
237 $this->prefixColumnsWithTable
= (bool) $prefixColumnsWithTable;
244 * @param string|array $name
246 * @param string|array $columns
247 * @param string $type one of the JOIN_* constants
248 * @throws Exception\InvalidArgumentException
251 public function join($name, $on, $columns = self
::SQL_STAR
, $type = self
::JOIN_INNER
)
253 if (is_array($name) && (!is_string(key($name)) ||
count($name) !== 1)) {
254 throw new Exception\
InvalidArgumentException(
255 sprintf("join() expects '%s' as an array is a single element associative array", array_shift($name))
258 if (!is_array($columns)) {
259 $columns = array($columns);
261 $this->joins
[] = array(
264 'columns' => $columns,
271 * Create where clause
273 * @param Where|\Closure|string|array|Predicate\PredicateInterface $predicate
274 * @param string $combination One of the OP_* constants from Predicate\PredicateSet
275 * @throws Exception\InvalidArgumentException
278 public function where($predicate, $combination = Predicate\PredicateSet
::OP_AND
)
280 if ($predicate instanceof Where
) {
281 $this->where
= $predicate;
282 } elseif ($predicate instanceof Predicate\PredicateInterface
) {
283 $this->where
->addPredicate($predicate, $combination);
284 } elseif ($predicate instanceof \Closure
) {
285 $predicate($this->where
);
287 if (is_string($predicate)) {
288 // String $predicate should be passed as an expression
289 $predicate = (strpos($predicate, Expression
::PLACEHOLDER
) !== false)
290 ?
new Predicate\
Expression($predicate) : new Predicate\
Literal($predicate);
291 $this->where
->addPredicate($predicate, $combination);
292 } elseif (is_array($predicate)) {
294 foreach ($predicate as $pkey => $pvalue) {
295 // loop through predicates
297 if (is_string($pkey) && strpos($pkey, '?') !== false) {
298 // First, process strings that the abstraction replacement character ?
299 // as an Expression predicate
300 $predicate = new Predicate\
Expression($pkey, $pvalue);
302 } elseif (is_string($pkey)) {
303 // Otherwise, if still a string, do something intelligent with the PHP type provided
305 if ($pvalue === null) {
306 // map PHP null to SQL IS NULL expression
307 $predicate = new Predicate\
IsNull($pkey, $pvalue);
308 } elseif (is_array($pvalue)) {
309 // if the value is an array, assume IN() is desired
310 $predicate = new Predicate\
In($pkey, $pvalue);
311 } elseif ($pvalue instanceof Predicate\PredicateInterface
) {
313 throw new Exception\
InvalidArgumentException(
314 'Using Predicate must not use string keys'
317 // otherwise assume that array('foo' => 'bar') means "foo" = 'bar'
318 $predicate = new Predicate\
Operator($pkey, Predicate\Operator
::OP_EQ
, $pvalue);
320 } elseif ($pvalue instanceof Predicate\PredicateInterface
) {
321 // Predicate type is ok
322 $predicate = $pvalue;
324 // must be an array of expressions (with int-indexed array)
325 $predicate = (strpos($pvalue, Expression
::PLACEHOLDER
) !== false)
326 ?
new Predicate\
Expression($pvalue) : new Predicate\
Literal($pvalue);
328 $this->where
->addPredicate($predicate, $combination);
335 public function group($group)
337 if (is_array($group)) {
338 foreach ($group as $o) {
342 $this->group
[] = $group;
348 * Create where clause
350 * @param Where|\Closure|string|array $predicate
351 * @param string $combination One of the OP_* constants from Predicate\PredicateSet
354 public function having($predicate, $combination = Predicate\PredicateSet
::OP_AND
)
356 if ($predicate instanceof Having
) {
357 $this->having
= $predicate;
358 } elseif ($predicate instanceof \Closure
) {
359 $predicate($this->having
);
361 if (is_string($predicate)) {
362 $predicate = new Predicate\
Expression($predicate);
363 $this->having
->addPredicate($predicate, $combination);
364 } elseif (is_array($predicate)) {
365 foreach ($predicate as $pkey => $pvalue) {
366 if (is_string($pkey) && strpos($pkey, '?') !== false) {
367 $predicate = new Predicate\
Expression($pkey, $pvalue);
368 } elseif (is_string($pkey)) {
369 $predicate = new Predicate\
Operator($pkey, Predicate\Operator
::OP_EQ
, $pvalue);
371 $predicate = new Predicate\
Expression($pvalue);
373 $this->having
->addPredicate($predicate, $combination);
381 * @param string|array $order
384 public function order($order)
386 if (is_string($order)) {
387 if (strpos($order, ',') !== false) {
388 $order = preg_split('#,\s+#', $order);
390 $order = (array) $order;
392 } elseif (!is_array($order)) {
393 $order = array($order);
395 foreach ($order as $k => $v) {
397 $this->order
[$k] = $v;
409 public function limit($limit)
411 if (!is_numeric($limit)) {
412 throw new Exception\
InvalidArgumentException(sprintf(
413 '%s expects parameter to be numeric, "%s" given',
415 (is_object($limit) ?
get_class($limit) : gettype($limit))
419 $this->limit
= $limit;
427 public function offset($offset)
429 if (!is_numeric($offset)) {
430 throw new Exception\
InvalidArgumentException(sprintf(
431 '%s expects parameter to be numeric, "%s" given',
433 (is_object($offset) ?
get_class($offset) : gettype($offset))
437 $this->offset
= $offset;
442 * @param Select $select
443 * @param string $type
444 * @param string $modifier
446 * @throws Exception\InvalidArgumentException
448 public function combine(Select
$select, $type = self
::COMBINE_UNION
, $modifier = '')
450 if ($this->combine
!== array()) {
451 throw new Exception\
InvalidArgumentException('This Select object is already combined and cannot be combined with multiple Selects objects');
453 $this->combine
= array(
456 'modifier' => $modifier
462 * @param string $part
464 * @throws Exception\InvalidArgumentException
466 public function reset($part)
470 if ($this->tableReadOnly
) {
471 throw new Exception\
InvalidArgumentException(
472 'Since this object was created with a table and/or schema in the constructor, it is read only.'
477 case self
::QUANTIFIER
:
478 $this->quantifier
= null;
481 $this->columns
= array();
484 $this->joins
= array();
487 $this->where
= new Where
;
493 $this->having
= new Having
;
499 $this->offset
= null;
505 $this->combine
= array();
511 public function setSpecification($index, $specification)
513 if (!method_exists($this, 'process' . $index)) {
514 throw new Exception\
InvalidArgumentException('Not a valid specification name.');
516 $this->specifications
[$index] = $specification;
520 public function getRawState($key = null)
523 self
::TABLE
=> $this->table
,
524 self
::QUANTIFIER
=> $this->quantifier
,
525 self
::COLUMNS
=> $this->columns
,
526 self
::JOINS
=> $this->joins
,
527 self
::WHERE
=> $this->where
,
528 self
::ORDER
=> $this->order
,
529 self
::GROUP
=> $this->group
,
530 self
::HAVING
=> $this->having
,
531 self
::LIMIT
=> $this->limit
,
532 self
::OFFSET
=> $this->offset
,
533 self
::COMBINE
=> $this->combine
535 return (isset($key) && array_key_exists($key, $rawState)) ?
$rawState[$key] : $rawState;
541 * @param AdapterInterface $adapter
542 * @param StatementContainerInterface $statementContainer
545 public function prepareStatement(AdapterInterface
$adapter, StatementContainerInterface
$statementContainer)
547 // ensure statement has a ParameterContainer
548 $parameterContainer = $statementContainer->getParameterContainer();
549 if (!$parameterContainer instanceof ParameterContainer
) {
550 $parameterContainer = new ParameterContainer();
551 $statementContainer->setParameterContainer($parameterContainer);
555 $parameters = array();
556 $platform = $adapter->getPlatform();
557 $driver = $adapter->getDriver();
559 foreach ($this->specifications
as $name => $specification) {
560 $parameters[$name] = $this->{'process' . $name}($platform, $driver, $parameterContainer, $sqls, $parameters);
561 if ($specification && is_array($parameters[$name])) {
562 $sqls[$name] = $this->createSqlFromSpecificationAndParameters($specification, $parameters[$name]);
566 $sql = implode(' ', $sqls);
568 $statementContainer->setSql($sql);
573 * Get SQL string for statement
575 * @param null|PlatformInterface $adapterPlatform If null, defaults to Sql92
578 public function getSqlString(PlatformInterface
$adapterPlatform = null)
580 // get platform, or create default
581 $adapterPlatform = ($adapterPlatform) ?
: new AdapterSql92Platform
;
584 $parameters = array();
586 foreach ($this->specifications
as $name => $specification) {
587 $parameters[$name] = $this->{'process' . $name}($adapterPlatform, null, null, $sqls, $parameters);
588 if ($specification && is_array($parameters[$name])) {
589 $sqls[$name] = $this->createSqlFromSpecificationAndParameters($specification, $parameters[$name]);
593 $sql = implode(' ', $sqls);
598 * Returns whether the table is read only or not.
602 public function isTableReadOnly()
604 return $this->tableReadOnly
;
608 * Render table with alias in from/join parts
610 * @todo move TableIdentifier concatination here
611 * @param string $table
612 * @param string $alias
615 protected function renderTable($table, $alias = null)
619 $sql .= ' AS ' . $alias;
624 protected function processStatementStart(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
626 if ($this->combine
!== array()) {
631 protected function processStatementEnd(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
633 if ($this->combine
!== array()) {
639 * Process the select part
641 * @param PlatformInterface $platform
642 * @param DriverInterface $driver
643 * @param ParameterContainer $parameterContainer
646 protected function processSelect(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
654 $table = $this->table
;
655 $schema = $alias = null;
657 if (is_array($table)) {
658 $alias = key($this->table
);
659 $table = current($this->table
);
662 // create quoted table name to use in columns processing
663 if ($table instanceof TableIdentifier
) {
664 list($table, $schema) = $table->getTableAndSchema();
667 if ($table instanceof Select
) {
668 $table = '(' . $this->processSubselect($table, $platform, $driver, $parameterContainer) . ')';
670 $table = $platform->quoteIdentifier($table);
674 $table = $platform->quoteIdentifier($schema) . $platform->getIdentifierSeparator() . $table;
678 $fromTable = $platform->quoteIdentifier($alias);
679 $table = $this->renderTable($table, $fromTable);
684 if ($this->prefixColumnsWithTable
) {
685 $fromTable .= $platform->getIdentifierSeparator();
690 // process table columns
692 foreach ($this->columns
as $columnIndexOrAs => $column) {
695 if ($column === self
::SQL_STAR
) {
696 $columns[] = array($fromTable . self
::SQL_STAR
);
700 if ($column instanceof Expression
) {
701 $columnParts = $this->processExpression(
705 $this->processInfo
['paramPrefix'] . ((is_string($columnIndexOrAs)) ?
$columnIndexOrAs : 'column')
707 if ($parameterContainer) {
708 $parameterContainer->merge($columnParts->getParameterContainer());
710 $columnName .= $columnParts->getSql();
712 $columnName .= $fromTable . $platform->quoteIdentifier($column);
715 // process As portion
716 if (is_string($columnIndexOrAs)) {
717 $columnAs = $platform->quoteIdentifier($columnIndexOrAs);
718 } elseif (stripos($columnName, ' as ') === false) {
719 $columnAs = (is_string($column)) ?
$platform->quoteIdentifier($column) : 'Expression' . $expr++
;
721 $columns[] = (isset($columnAs)) ?
array($columnName, $columnAs) : array($columnName);
724 $separator = $platform->getIdentifierSeparator();
726 // process join columns
727 foreach ($this->joins
as $join) {
728 foreach ($join['columns'] as $jKey => $jColumn) {
730 if ($jColumn instanceof ExpressionInterface
) {
731 $jColumnParts = $this->processExpression(
735 $this->processInfo
['paramPrefix'] . ((is_string($jKey)) ?
$jKey : 'column')
737 if ($parameterContainer) {
738 $parameterContainer->merge($jColumnParts->getParameterContainer());
740 $jColumns[] = $jColumnParts->getSql();
742 $name = (is_array($join['name'])) ?
key($join['name']) : $name = $join['name'];
743 if ($name instanceof TableIdentifier
) {
744 $name = $platform->quoteIdentifier($name->getSchema()) . $separator . $platform->quoteIdentifier($name->getTable());
746 $name = $platform->quoteIdentifier($name);
748 $jColumns[] = $name . $separator . $platform->quoteIdentifierInFragment($jColumn);
750 if (is_string($jKey)) {
751 $jColumns[] = $platform->quoteIdentifier($jKey);
752 } elseif ($jColumn !== self
::SQL_STAR
) {
753 $jColumns[] = $platform->quoteIdentifier($jColumn);
755 $columns[] = $jColumns;
759 if ($this->quantifier
) {
760 if ($this->quantifier
instanceof Expression
) {
761 $quantifierParts = $this->processExpression($this->quantifier
, $platform, $driver, 'quantifier');
762 if ($parameterContainer) {
763 $parameterContainer->merge($quantifierParts->getParameterContainer());
765 $quantifier = $quantifierParts->getSql();
767 $quantifier = $this->quantifier
;
771 if (isset($quantifier)) {
772 return array($quantifier, $columns, $table);
774 return array($columns, $table);
778 protected function processJoins(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
785 $joinSpecArgArray = array();
786 foreach ($this->joins
as $j => $join) {
787 $joinSpecArgArray[$j] = array();
792 $joinSpecArgArray[$j][] = strtoupper($join['type']);
795 if (is_array($join['name'])) {
796 $joinName = current($join['name']);
797 $joinAs = $platform->quoteIdentifier(key($join['name']));
799 $joinName = $join['name'];
801 if ($joinName instanceof TableIdentifier
) {
802 $joinName = $joinName->getTableAndSchema();
803 $joinName = $platform->quoteIdentifier($joinName[1]) . $platform->getIdentifierSeparator() . $platform->quoteIdentifier($joinName[0]);
805 if ($joinName instanceof Select
) {
806 $joinName = '(' . $joinName->processSubSelect($joinName, $platform, $driver, $parameterContainer) . ')';
808 $joinName = $platform->quoteIdentifier($joinName);
811 $joinSpecArgArray[$j][] = (isset($joinAs)) ?
$joinName . ' AS ' . $joinAs : $joinName;
814 // note: for Expression objects, pass them to processExpression with a prefix specific to each join (used for named parameters)
815 $joinSpecArgArray[$j][] = ($join['on'] instanceof ExpressionInterface
)
816 ?
$this->processExpression($join['on'], $platform, $driver, $this->processInfo
['paramPrefix'] . 'join' . ($j+
1) . 'part')
817 : $platform->quoteIdentifierInFragment($join['on'], array('=', 'AND', 'OR', '(', ')', 'BETWEEN', '<', '>')); // on
818 if ($joinSpecArgArray[$j][2] instanceof StatementContainerInterface
) {
819 if ($parameterContainer) {
820 $parameterContainer->merge($joinSpecArgArray[$j][2]->getParameterContainer());
822 $joinSpecArgArray[$j][2] = $joinSpecArgArray[$j][2]->getSql();
826 return array($joinSpecArgArray);
829 protected function processWhere(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
831 if ($this->where
->count() == 0) {
834 $whereParts = $this->processExpression($this->where
, $platform, $driver, $this->processInfo
['paramPrefix'] . 'where');
835 if ($parameterContainer) {
836 $parameterContainer->merge($whereParts->getParameterContainer());
838 return array($whereParts->getSql());
841 protected function processGroup(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
843 if ($this->group
=== null) {
846 // process table columns
848 foreach ($this->group
as $column) {
850 if ($column instanceof Expression
) {
851 $columnParts = $this->processExpression($column, $platform, $driver, $this->processInfo
['paramPrefix'] . 'group');
852 if ($parameterContainer) {
853 $parameterContainer->merge($columnParts->getParameterContainer());
855 $columnSql .= $columnParts->getSql();
857 $columnSql .= $platform->quoteIdentifierInFragment($column);
859 $groups[] = $columnSql;
861 return array($groups);
864 protected function processHaving(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
866 if ($this->having
->count() == 0) {
869 $whereParts = $this->processExpression($this->having
, $platform, $driver, $this->processInfo
['paramPrefix'] . 'having');
870 if ($parameterContainer) {
871 $parameterContainer->merge($whereParts->getParameterContainer());
873 return array($whereParts->getSql());
876 protected function processOrder(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
878 if (empty($this->order
)) {
882 foreach ($this->order
as $k => $v) {
883 if ($v instanceof Expression
) {
884 /** @var $orderParts \Zend\Db\Adapter\StatementContainer */
885 $orderParts = $this->processExpression($v, $platform, $driver);
886 if ($parameterContainer) {
887 $parameterContainer->merge($orderParts->getParameterContainer());
889 $orders[] = array($orderParts->getSql());
893 if (strpos($v, ' ') !== false) {
894 list($k, $v) = preg_split('# #', $v, 2);
897 $v = self
::ORDER_ASCENDING
;
900 if (strtoupper($v) == self
::ORDER_DESCENDING
) {
901 $orders[] = array($platform->quoteIdentifierInFragment($k), self
::ORDER_DESCENDING
);
903 $orders[] = array($platform->quoteIdentifierInFragment($k), self
::ORDER_ASCENDING
);
906 return array($orders);
909 protected function processLimit(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
911 if ($this->limit
=== null) {
915 $limit = (int) $this->limit
;
918 $sql = $driver->formatParameterName('limit');
919 $parameterContainer->offsetSet('limit', $limit, ParameterContainer
::TYPE_INTEGER
);
921 $sql = $platform->quoteValue($limit);
927 protected function processOffset(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
929 if ($this->offset
=== null) {
933 $offset = (int) $this->offset
;
936 $parameterContainer->offsetSet('offset', $offset, ParameterContainer
::TYPE_INTEGER
);
937 return array($driver->formatParameterName('offset'));
940 return array($platform->quoteValue($offset));
943 protected function processCombine(PlatformInterface
$platform, DriverInterface
$driver = null, ParameterContainer
$parameterContainer = null)
945 if ($this->combine
== array()) {
949 $type = $this->combine
['type'];
950 if ($this->combine
['modifier']) {
951 $type .= ' ' . $this->combine
['modifier'];
953 $type = strtoupper($type);
956 $sql = $this->processSubSelect($this->combine
['select'], $platform, $driver, $parameterContainer);
957 return array($type, $sql);
961 $this->processSubSelect($this->combine
['select'], $platform)
966 * Variable overloading
968 * @param string $name
969 * @throws Exception\InvalidArgumentException
972 public function __get($name)
974 switch (strtolower($name)) {
978 return $this->having
;
980 throw new Exception\
InvalidArgumentException('Not a valid magic property for this object');
987 * Resets the where object each time the Select is cloned.
991 public function __clone()
993 $this->where
= clone $this->where
;
994 $this->having
= clone $this->having
;