PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Varien/Db/Select.php

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