*/
class Varien_Db_Select extends Zend_Db_Select
{
const TYPE_CONDITION = 'TYPE_CONDITION';
const STRAIGHT_JOIN_ON = 'straight_join';
const STRAIGHT_JOIN = 'straightjoin';
const SQL_STRAIGHT_JOIN = 'STRAIGHT_JOIN';
/**
* Class constructor
*
* @param Zend_Db_Adapter_Abstract $adapter
*/
public function __construct(Zend_Db_Adapter_Abstract $adapter)
{
parent::__construct($adapter);
if (!in_array(self::STRAIGHT_JOIN_ON, self::$_joinTypes)) {
self::$_joinTypes[] = self::STRAIGHT_JOIN_ON;
self::$_partsInit = array(self::STRAIGHT_JOIN => false) + self::$_partsInit;
}
}
/**
* Adds a WHERE condition to the query by AND.
*
* If a value is passed as the second param, it will be quoted
* and replaced into the condition wherever a question-mark
* appears. Array values are quoted and comma-separated.
*
*
* // simplest but non-secure
* $select->where("id = $id");
*
* // secure (ID is quoted but matched anyway)
* $select->where('id = ?', $id);
*
* // alternatively, with named binding
* $select->where('id = :id');
*
*
* Note that it is more correct to use named bindings in your
* queries for values other than strings. When you use named
* bindings, don't forget to pass the values when actually
* making a query:
*
*
* $db->fetchAll($select, array('id' => 5));
*
*
* @param string $cond The WHERE condition.
* @param string $value OPTIONAL A single value to quote into the condition.
* @param constant $type OPTIONAL The type of the given value
* @return Zend_Db_Select This Zend_Db_Select object.
*/
public function where($cond, $value = null, $type = null)
{
if (is_null($value) && is_null($type)) {
$value = '';
}
/**
* Additional internal type used for really null value
*/
if ($type == self::TYPE_CONDITION) {
$type = null;
}
if (is_array($value)) {
$cond = $this->_adapter->quoteInto($cond, $value);
$value = null;
}
return parent::where($cond, $value, $type);
}
/**
* Reset unused LEFT JOIN(s)
*
* @return Varien_Db_Select
*/
public function resetJoinLeft()
{
foreach ($this->_parts[self::FROM] as $tableId => $tableProp) {
if ($tableProp['joinType'] == self::LEFT_JOIN) {
$useJoin = false;
foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
list($correlationName, $column) = $columnEntry;
if ($column instanceof Zend_Db_Expr) {
if ($this->_findTableInCond($tableId, $column)
|| $this->_findTableInCond($tableProp['tableName'], $column)) {
$useJoin = true;
}
}
else {
if ($correlationName == $tableId) {
$useJoin = true;
}
}
}
foreach ($this->_parts[self::WHERE] as $where) {
if ($this->_findTableInCond($tableId, $where)
|| $this->_findTableInCond($tableProp['tableName'], $where)) {
$useJoin = true;
}
}
$joinUseInCond = $useJoin;
$joinInTables = array();
foreach ($this->_parts[self::FROM] as $tableCorrelationName => $table) {
if ($tableCorrelationName == $tableId) {
continue;
}
if (!empty($table['joinCondition'])) {
if ($this->_findTableInCond($tableId, $table['joinCondition'])
|| $this->_findTableInCond($tableProp['tableName'], $table['joinCondition'])) {
$useJoin = true;
$joinInTables[] = $tableCorrelationName;
}
}
}
if (!$useJoin) {
unset($this->_parts[self::FROM][$tableId]);
}
else {
$this->_parts[self::FROM][$tableId]['useInCond'] = $joinUseInCond;
$this->_parts[self::FROM][$tableId]['joinInTables'] = $joinInTables;
}
}
}
$this->_resetJoinLeft();
return $this;
}
protected function _resetJoinLeft()
{
foreach ($this->_parts[self::FROM] as $tableId => $tableProp) {
if ($tableProp['joinType'] == self::LEFT_JOIN) {
if ($tableProp['useInCond']) {
continue;
}
$used = false;
foreach ($tableProp['joinInTables'] as $table) {
if (isset($this->_parts[self::FROM][$table])) {
$used = true;
}
}
if (!$used) {
unset($this->_parts[self::FROM][$tableId]);
return $this->_resetJoinLeft();
}
}
}
return $this;
}
/**
* Find table name in condition (where, column)
*
* @param string $table
* @param string $cond
* @return bool
*/
protected function _findTableInCond($table, $cond)
{
$quote = $this->_adapter->getQuoteIdentifierSymbol();
if (strpos($cond, $quote . $table . $quote . '.') !== false) {
return true;
}
$position = 0;
$result = 0;
$needle = array();
while (is_integer($result)) {
$result = strpos($cond, $table . '.', $position);
if (is_integer($result)) {
$needle[] = $result;
$position = ($result + strlen($table) + 1);
}
}
if (!$needle) {
return false;
}
foreach ($needle as $position) {
if ($position == 0) {
return true;
}
if (!preg_match('#[a-z0-9_]#is', substr($cond, $position - 1, 1))) {
return true;
}
}
return false;
}
/**
* Cross Table Update From Current select
*
* @param string|array $table
* @return string
*/
public function crossUpdateFromSelect($table) {
if (!is_array($table)) {
$table = array($table => $table);
}
$keys = array_keys($table);
$tableAlias = $keys[0];
$tableName = $table[$keys[0]];
$sql = "UPDATE `{$tableName}`";
if ($tableAlias != $tableName) {
$sql .= " AS `{$tableAlias}`";
}
// render FROM
$from = array();
foreach ($this->_parts[self::FROM] as $correlationName => $table) {
$tmp = '';
$tmp .= ' ' . strtoupper($table['joinType']) . ' ';
$tmp .= $this->_getQuotedSchema($table['schema']);
$tmp .= $this->_getQuotedTable($table['tableName'], $correlationName);
// Add join conditions (if applicable)
if (! empty($table['joinCondition'])) {
$tmp .= ' ' . self::SQL_ON . ' ' . $table['joinCondition'];
}
// Add the table name and condition add to the list
$from[] = $tmp;
}
// Add the list of all joins
if (!empty($from)) {
$sql .= implode("\n", $from);
}
// render UPDATE SET
$columns = array();
foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
list($correlationName, $column, $alias) = $columnEntry;
if (empty($alias)) {
$alias = $column;
}
if (!$column instanceof Zend_Db_Expr && !empty($correlationName)) {
$column = $this->_adapter->quoteIdentifier(array($correlationName, $column));
}
$columns[] = $this->_adapter->quoteIdentifier(array($tableAlias, $alias))
. " = {$column}";
}
$sql .= "\n SET " . implode(', ', $columns) . "\n";
// render WHERE
$sql = $this->_renderWhere($sql);
return $sql;
}
/**
* Insert to table from current select
*
* @param string $tableName
* @param array $fields
* @param bool $onDuplicate
* @return string
*/
public function insertFromSelect($tableName, $fields = array(), $onDuplicate = true)
{
$sql = "INSERT INTO `{$tableName}` ";
$inserFields = array();
foreach ($fields as $key => $field) {
if (is_string($field)) {
$inserFields[] = $field;
} else {
$inserFields[] = $key;
}
}
if ($inserFields) {
$sql .= "(`".join('`,`', $inserFields) . "`) ";
}
$sql .= $this->assemble();
if ($onDuplicate && $fields) {
$sql .= " ON DUPLICATE KEY UPDATE";
$updateFields = array();
foreach ($fields as $key => $field) {
if (is_string($field)) {
$field = $this->_adapter->quoteIdentifier($field);
$updateFields[] = "{$field}=VALUES({$field})";
}
}
$sql .= " " . join(', ', $updateFields);
}
return $sql;
}
/**
* Generate INSERT IGNORE query to the table from current select
*
* @param string $tableName
* @param array $fields
* @return string
*/
public function insertIgnoreFromSelect($tableName, $fields = array())
{
$insertFields = '';
if ($fields) {
$quotedFields = array_map(array($this->getAdapter(), 'quoteIdentifier'), $fields);
$insertFields = '(' . join(',', $quotedFields) . ') ';
}
return sprintf('INSERT IGNORE %s %s%s',
$this->getAdapter()->quoteIdentifier($tableName),
$insertFields,
$this->assemble()
);
}
/**
* Retrieve DELETE query from select
*
* @param string $table The table name or alias
* @return string
*/
public function deleteFromSelect($table) {
$partsInit = self::$_partsInit;
unset($partsInit[self::DISTINCT]);
unset($partsInit[self::COLUMNS]);
$sql = 'DELETE ' . $table;
foreach (array_keys($partsInit) as $part) {
$method = '_render' . ucfirst($part);
if (method_exists($this, $method)) {
$sql = $this->$method($sql);
}
}
return $sql;
}
/**
* Modify (hack) part of the structured information for the currect query
*
* @param string $part
* @param mixed $value
* @return Varien_Db_Select
*/
public function setPart($part, $value)
{
$part = strtolower($part);
if (!array_key_exists($part, $this->_parts)) {
throw new Zend_Db_Select_Exception("Invalid Select part '$part'");
}
$this->_parts[$part] = $value;
return $this;
}
/**
* Add a STRAIGHT_JOIN table and colums to the query (MySQL only).
* STRAIGHT_JOIN is similar to JOIN, except that the left table
* is always read before the right table. This can be used for those
* (few) cases for which the join optimizer puts the tables in the wrong order
*
* The $name and $cols parameters follow the same logic
* as described in the from() method.
*
* @param array|string|Zend_Db_Expr $name The table name.
* @param string $cond Join on this condition.
* @param array|string $cols The columns to select from the joined table.
* @param string $schema The database name to specify, if any.
* @return Zend_Db_Select This Zend_Db_Select object.
*/
public function joinStraight($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
{
return $this->_join(self::STRAIGHT_JOIN_ON, $name, $cond, $cols, $schema);
}
/**
* Use a STRAIGHT_JOIN for the SQL Select
*
* @param bool $flag Whether or not the SELECT use STRAIGHT_JOIN (default true).
* @return Zend_Db_Select This Zend_Db_Select object.
*/
public function useStraightJoin($flag = true)
{
$this->_parts[self::STRAIGHT_JOIN] = (bool) $flag;
return $this;
}
/**
* Render STRAIGHT_JOIN clause
*
* @param string $sql SQL query
* @return string
*/
protected function _renderStraightjoin($sql)
{
if (!empty($this->_parts[self::STRAIGHT_JOIN])) {
$sql .= ' ' . self::SQL_STRAIGHT_JOIN;
}
return $sql;
}
}