PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Varien/Db/Select.php

https://github.com/gryzz/crystal_magento
PHP | 436 lines | 245 code | 44 blank | 147 comment | 50 complexity | 2d70c20c971c095c10849a3da3a6aa4b MD5 | raw file
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Varien
  22. * @package Varien_Db
  23. * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Class for SQL SELECT generation and results.
  28. *
  29. * @category Varien
  30. * @package Varien_Db
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Varien_Db_Select extends Zend_Db_Select
  34. {
  35. const STRAIGHT_JOIN_ON = 'straight_join';
  36. const STRAIGHT_JOIN = 'straightjoin';
  37. const SQL_STRAIGHT_JOIN = 'STRAIGHT_JOIN';
  38. /**
  39. * Class constructor
  40. *
  41. * @param Zend_Db_Adapter_Abstract $adapter
  42. */
  43. public function __construct(Zend_Db_Adapter_Abstract $adapter)
  44. {
  45. parent::__construct($adapter);
  46. self::$_joinTypes[] = self::STRAIGHT_JOIN_ON;
  47. self::$_partsInit = array(self::STRAIGHT_JOIN => false) + self::$_partsInit;
  48. }
  49. /**
  50. * Adds a WHERE condition to the query by AND.
  51. *
  52. * If a value is passed as the second param, it will be quoted
  53. * and replaced into the condition wherever a question-mark
  54. * appears. Array values are quoted and comma-separated.
  55. *
  56. * <code>
  57. * // simplest but non-secure
  58. * $select->where("id = $id");
  59. *
  60. * // secure (ID is quoted but matched anyway)
  61. * $select->where('id = ?', $id);
  62. *
  63. * // alternatively, with named binding
  64. * $select->where('id = :id');
  65. * </code>
  66. *
  67. * Note that it is more correct to use named bindings in your
  68. * queries for values other than strings. When you use named
  69. * bindings, don't forget to pass the values when actually
  70. * making a query:
  71. *
  72. * <code>
  73. * $db->fetchAll($select, array('id' => 5));
  74. * </code>
  75. *
  76. * @param string $cond The WHERE condition.
  77. * @param string $value OPTIONAL A single value to quote into the condition.
  78. * @param constant $type OPTIONAL The type of the given value
  79. * @return Zend_Db_Select This Zend_Db_Select object.
  80. */
  81. public function where($cond, $value = null, $type = null)
  82. {
  83. if (is_null($value) && is_null($type)) {
  84. $value = '';
  85. }
  86. if (is_array($value)) {
  87. $cond = $this->_adapter->quoteInto($cond, $value);
  88. $value = null;
  89. }
  90. return parent::where($cond, $value, $type);
  91. }
  92. /**
  93. * Reset unused LEFT JOIN(s)
  94. *
  95. * @return Varien_Db_Select
  96. */
  97. public function resetJoinLeft()
  98. {
  99. foreach ($this->_parts[self::FROM] as $tableId => $tableProp) {
  100. if ($tableProp['joinType'] == self::LEFT_JOIN) {
  101. $useJoin = false;
  102. foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
  103. list($correlationName, $column) = $columnEntry;
  104. if ($column instanceof Zend_Db_Expr) {
  105. if ($this->_findTableInCond($tableId, $column)
  106. || $this->_findTableInCond($tableProp['tableName'], $column)) {
  107. $useJoin = true;
  108. }
  109. }
  110. else {
  111. if ($correlationName == $tableId) {
  112. $useJoin = true;
  113. }
  114. }
  115. }
  116. foreach ($this->_parts[self::WHERE] as $where) {
  117. if ($this->_findTableInCond($tableId, $where)
  118. || $this->_findTableInCond($tableProp['tableName'], $where)) {
  119. $useJoin = true;
  120. }
  121. }
  122. $joinUseInCond = $useJoin;
  123. $joinInTables = array();
  124. foreach ($this->_parts[self::FROM] as $tableCorrelationName => $table) {
  125. if ($tableCorrelationName == $tableId) {
  126. continue;
  127. }
  128. if (!empty($table['joinCondition'])) {
  129. if ($this->_findTableInCond($tableId, $table['joinCondition'])
  130. || $this->_findTableInCond($tableProp['tableName'], $table['joinCondition'])) {
  131. $useJoin = true;
  132. $joinInTables[] = $tableCorrelationName;
  133. }
  134. }
  135. }
  136. if (!$useJoin) {
  137. unset($this->_parts[self::FROM][$tableId]);
  138. }
  139. else {
  140. $this->_parts[self::FROM][$tableId]['useInCond'] = $joinUseInCond;
  141. $this->_parts[self::FROM][$tableId]['joinInTables'] = $joinInTables;
  142. }
  143. }
  144. }
  145. $this->_resetJoinLeft();
  146. return $this;
  147. }
  148. protected function _resetJoinLeft()
  149. {
  150. foreach ($this->_parts[self::FROM] as $tableId => $tableProp) {
  151. if ($tableProp['joinType'] == self::LEFT_JOIN) {
  152. if ($tableProp['useInCond']) {
  153. continue;
  154. }
  155. $used = false;
  156. foreach ($tableProp['joinInTables'] as $table) {
  157. if (isset($this->_parts[self::FROM][$table])) {
  158. $used = true;
  159. }
  160. }
  161. if (!$used) {
  162. unset($this->_parts[self::FROM][$tableId]);
  163. return $this->_resetJoinLeft();
  164. }
  165. }
  166. }
  167. return $this;
  168. }
  169. /**
  170. * Find table name in condition (where, column)
  171. *
  172. * @param string $table
  173. * @param string $cond
  174. * @return bool
  175. */
  176. protected function _findTableInCond($table, $cond)
  177. {
  178. $quote = $this->_adapter->getQuoteIdentifierSymbol();
  179. if (strpos($cond, $quote . $table . $quote . '.') !== false) {
  180. return true;
  181. }
  182. $position = 0;
  183. $result = 0;
  184. $needle = array();
  185. while (is_integer($result)) {
  186. $result = strpos($cond, $table . '.', $position);
  187. if (is_integer($result)) {
  188. $needle[] = $result;
  189. $position = ($result + strlen($table) + 1);
  190. }
  191. }
  192. if (!$needle) {
  193. return false;
  194. }
  195. foreach ($needle as $position) {
  196. if ($position == 0) {
  197. return true;
  198. }
  199. if (!preg_match('#[a-z0-9_]#is', substr($cond, $position - 1, 1))) {
  200. return true;
  201. }
  202. }
  203. return false;
  204. }
  205. /**
  206. * Cross Table Update From Current select
  207. *
  208. * @param string|array $table
  209. * @return string
  210. */
  211. public function crossUpdateFromSelect($table) {
  212. if (!is_array($table)) {
  213. $table = array($table => $table);
  214. }
  215. $keys = array_keys($table);
  216. $tableAlias = $keys[0];
  217. $tableName = $table[$keys[0]];
  218. $sql = "UPDATE `{$tableName}`";
  219. if ($tableAlias != $tableName) {
  220. $sql .= " AS `{$tableAlias}`";
  221. }
  222. // render FROM
  223. $from = array();
  224. foreach ($this->_parts[self::FROM] as $correlationName => $table) {
  225. $tmp = '';
  226. $tmp .= ' ' . strtoupper($table['joinType']) . ' ';
  227. $tmp .= $this->_getQuotedSchema($table['schema']);
  228. $tmp .= $this->_getQuotedTable($table['tableName'], $correlationName);
  229. // Add join conditions (if applicable)
  230. if (! empty($table['joinCondition'])) {
  231. $tmp .= ' ' . self::SQL_ON . ' ' . $table['joinCondition'];
  232. }
  233. // Add the table name and condition add to the list
  234. $from[] = $tmp;
  235. }
  236. // Add the list of all joins
  237. if (!empty($from)) {
  238. $sql .= implode("\n", $from);
  239. }
  240. // render UPDATE SET
  241. $columns = array();
  242. foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
  243. list($correlationName, $column, $alias) = $columnEntry;
  244. if (empty($alias)) {
  245. $alias = $column;
  246. }
  247. if (!$column instanceof Zend_Db_Expr && !empty($correlationName)) {
  248. $column = $this->_adapter->quoteIdentifier(array($correlationName, $column));
  249. }
  250. $columns[] = $this->_adapter->quoteIdentifier(array($tableAlias, $alias))
  251. . " = {$column}";
  252. }
  253. $sql .= "\n SET " . implode(', ', $columns) . "\n";
  254. // render WHERE
  255. $sql = $this->_renderWhere($sql);
  256. return $sql;
  257. }
  258. /**
  259. * Insert to table from current select
  260. *
  261. * @param string $tableName
  262. * @param array $fields
  263. * @param bool $onDuplicate
  264. * @return string
  265. */
  266. public function insertFromSelect($tableName, $fields = array(), $onDuplicate = true)
  267. {
  268. $sql = "INSERT INTO `{$tableName}` ";
  269. $inserFields = array();
  270. foreach ($fields as $key => $field) {
  271. if (is_string($field)) {
  272. $inserFields[] = $field;
  273. } else {
  274. $inserFields[] = $key;
  275. }
  276. }
  277. if ($inserFields) {
  278. $sql .= "(`".join('`,`', $inserFields) . "`) ";
  279. }
  280. $sql .= $this->assemble();
  281. if ($onDuplicate && $fields) {
  282. $sql .= " ON DUPLICATE KEY UPDATE";
  283. $updateFields = array();
  284. foreach ($fields as $key => $field) {
  285. if (is_string($field)) {
  286. $field = $this->_adapter->quoteIdentifier($field);
  287. $updateFields[] = "{$field}=VALUES({$field})";
  288. }
  289. }
  290. $sql .= " " . join(', ', $updateFields);
  291. }
  292. return $sql;
  293. }
  294. /**
  295. * Generate INSERT IGNORE query to the table from current select
  296. *
  297. * @param string $tableName
  298. * @param array $fields
  299. * @return string
  300. */
  301. public function insertIgnoreFromSelect($tableName, $fields = array())
  302. {
  303. $insertFields = '';
  304. if ($fields) {
  305. $quotedFields = array_map(array($this->getAdapter(), 'quoteIdentifier'), $fields);
  306. $insertFields = '(' . join(',', $quotedFields) . ') ';
  307. }
  308. return sprintf('INSERT IGNORE %s %s%s',
  309. $this->getAdapter()->quoteIdentifier($tableName),
  310. $insertFields,
  311. $this->assemble()
  312. );
  313. }
  314. /**
  315. * Retrieve DELETE query from select
  316. *
  317. * @param string $table The table name or alias
  318. * @return string
  319. */
  320. public function deleteFromSelect($table) {
  321. $partsInit = self::$_partsInit;
  322. unset($partsInit[self::DISTINCT]);
  323. unset($partsInit[self::COLUMNS]);
  324. $sql = 'DELETE ' . $table;
  325. foreach (array_keys($partsInit) as $part) {
  326. $method = '_render' . ucfirst($part);
  327. if (method_exists($this, $method)) {
  328. $sql = $this->$method($sql);
  329. }
  330. }
  331. return $sql;
  332. }
  333. /**
  334. * Modify (hack) part of the structured information for the currect query
  335. *
  336. * @param string $part
  337. * @param mixed $value
  338. * @return Varien_Db_Select
  339. */
  340. public function setPart($part, $value)
  341. {
  342. $part = strtolower($part);
  343. if (!array_key_exists($part, $this->_parts)) {
  344. throw new Zend_Db_Select_Exception("Invalid Select part '$part'");
  345. }
  346. $this->_parts[$part] = $value;
  347. return $this;
  348. }
  349. /**
  350. * Add a STRAIGHT_JOIN table and colums to the query (MySQL only).
  351. * STRAIGHT_JOIN is similar to JOIN, except that the left table
  352. * is always read before the right table. This can be used for those
  353. * (few) cases for which the join optimizer puts the tables in the wrong order
  354. *
  355. * The $name and $cols parameters follow the same logic
  356. * as described in the from() method.
  357. *
  358. * @param array|string|Zend_Db_Expr $name The table name.
  359. * @param string $cond Join on this condition.
  360. * @param array|string $cols The columns to select from the joined table.
  361. * @param string $schema The database name to specify, if any.
  362. * @return Zend_Db_Select This Zend_Db_Select object.
  363. */
  364. public function joinStraight($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
  365. {
  366. return $this->_join(self::STRAIGHT_JOIN_ON, $name, $cond, $cols, $schema);
  367. }
  368. /**
  369. * Use a STRAIGHT_JOIN for the SQL Select
  370. *
  371. * @param bool $flag Whether or not the SELECT use STRAIGHT_JOIN (default true).
  372. * @return Zend_Db_Select This Zend_Db_Select object.
  373. */
  374. public function useStraightJoin($flag = true)
  375. {
  376. $this->_parts[self::STRAIGHT_JOIN] = (bool) $flag;
  377. return $this;
  378. }
  379. /**
  380. * Render STRAIGHT_JOIN clause
  381. *
  382. * @param string $sql SQL query
  383. * @return string
  384. */
  385. protected function _renderStraightjoin($sql)
  386. {
  387. if (!empty($this->_parts[self::STRAIGHT_JOIN])) {
  388. $sql .= ' ' . self::SQL_STRAIGHT_JOIN;
  389. }
  390. return $sql;
  391. }
  392. }