PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Varien/Db/Adapter/Pdo/Mysql.php

https://bitbucket.org/andrewjleavitt/magestudy
PHP | 1808 lines | 1612 code | 35 blank | 161 comment | 24 complexity | 0e576926f2fe060e9c04ea42422607ee MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL

Large files files are truncated, but you can click here to view the full 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) 2008 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. * Mysql PDO DB adapter
  28. */
  29. class Varien_Db_Adapter_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Mysql
  30. {
  31. const DEBUG_CONNECT = 0;
  32. const DEBUG_TRANSACTION = 1;
  33. const DEBUG_QUERY = 2;
  34. const ISO_DATE_FORMAT = 'yyyy-MM-dd';
  35. const ISO_DATETIME_FORMAT = 'yyyy-MM-dd HH-mm-ss';
  36. const DDL_DESCRIBE = 1;
  37. const DDL_CREATE = 2;
  38. const DDL_INDEX = 3;
  39. const DDL_FOREIGN_KEY = 4;
  40. const DDL_CACHE_PREFIX = 'DB_PDO_MYSQL_DDL';
  41. const DDL_CACHE_TAG = 'DB_PDO_MYSQL_DDL';
  42. /**
  43. * Current Transaction Level
  44. *
  45. * @var int
  46. */
  47. protected $_transactionLevel = 0;
  48. /**
  49. * Set attribute to connection flag
  50. *
  51. * @var bool
  52. */
  53. protected $_connectionFlagsSet = false;
  54. /**
  55. * Tables DDL cache
  56. *
  57. * @var array
  58. */
  59. protected $_ddlCache = array();
  60. /**
  61. * SQL bind params
  62. *
  63. * @var array
  64. */
  65. protected $_bindParams = array();
  66. /**
  67. * Autoincrement for bind value
  68. *
  69. * @var int
  70. */
  71. protected $_bindIncrement = 0;
  72. /**
  73. * Write SQL debug data to file
  74. *
  75. * @var bool
  76. */
  77. protected $_debug = false;
  78. /**
  79. * Minimum query duration time to be logged
  80. *
  81. * @var unknown_type
  82. */
  83. protected $_logQueryTime = 0.05;
  84. /**
  85. * Log all queries (ignored minimum query duration time)
  86. *
  87. * @var bool
  88. */
  89. protected $_logAllQueries = false;
  90. /**
  91. * Add to log call stack data (backtrace)
  92. *
  93. * @var bool
  94. */
  95. protected $_logCallStack = false;
  96. /**
  97. * Path to SQL debug data log
  98. *
  99. * @var string
  100. */
  101. protected $_debugFile = 'var/debug/sql.txt';
  102. /**
  103. * Io File Adapter
  104. *
  105. * @var Varien_Io_File
  106. */
  107. protected $_debugIoAdapter;
  108. /**
  109. * Debug timer start value
  110. *
  111. * @var float
  112. */
  113. protected $_debugTimer = 0;
  114. /**
  115. * Cache frontend adapter instance
  116. *
  117. * @var Zend_Cache_Core
  118. */
  119. protected $_cacheAdapter;
  120. /**
  121. * DDL cache allowing flag
  122. * @var bool
  123. */
  124. protected $_isDdlCacheAllowed = true;
  125. /**
  126. * Begin new DB transaction for connection
  127. *
  128. * @return Varien_Db_Adapter_Pdo_Mysql
  129. */
  130. public function beginTransaction()
  131. {
  132. if ($this->_transactionLevel===0) {
  133. $this->_debugTimer();
  134. parent::beginTransaction();
  135. $this->_debugStat(self::DEBUG_TRANSACTION, 'BEGIN');
  136. }
  137. $this->_transactionLevel++;
  138. return $this;
  139. }
  140. /**
  141. * Commit DB transaction
  142. *
  143. * @return Varien_Db_Adapter_Pdo_Mysql
  144. */
  145. public function commit()
  146. {
  147. if ($this->_transactionLevel===1) {
  148. $this->_debugTimer();
  149. parent::commit();
  150. $this->_debugStat(self::DEBUG_TRANSACTION, 'COMMIT');
  151. }
  152. $this->_transactionLevel--;
  153. return $this;
  154. }
  155. /**
  156. * Rollback DB transaction
  157. *
  158. * @return Varien_Db_Adapter_Pdo_Mysql
  159. */
  160. public function rollback()
  161. {
  162. if ($this->_transactionLevel===1) {
  163. $this->_debugTimer();
  164. parent::rollback();
  165. $this->_debugStat(self::DEBUG_TRANSACTION, 'ROLLBACK');
  166. }
  167. $this->_transactionLevel--;
  168. return $this;
  169. }
  170. /**
  171. * Get adapter transaction level state. Return 0 if all transactions are complete
  172. *
  173. * @return int
  174. */
  175. public function getTransactionLevel()
  176. {
  177. return $this->_transactionLevel;
  178. }
  179. /**
  180. * Convert date to DB format
  181. *
  182. * @param mixed $date
  183. * @return string
  184. */
  185. public function convertDate($date)
  186. {
  187. if ($date instanceof Zend_Date) {
  188. return $date->toString(self::ISO_DATE_FORMAT);
  189. }
  190. return strftime('%Y-%m-%d', strtotime($date));
  191. }
  192. /**
  193. * Convert date and time to DB format
  194. *
  195. * @param mixed $date
  196. * @return string
  197. */
  198. public function convertDateTime($datetime)
  199. {
  200. if ($datetime instanceof Zend_Date) {
  201. return $datetime->toString(self::ISO_DATETIME_FORMAT);
  202. }
  203. return strftime('%Y-%m-%d %H:%M:%S', strtotime($datetime));
  204. }
  205. /**
  206. * Creates a PDO object and connects to the database.
  207. *
  208. */
  209. protected function _connect()
  210. {
  211. if ($this->_connection) {
  212. return;
  213. }
  214. if (!extension_loaded('pdo_mysql')) {
  215. throw new Zend_Db_Adapter_Exception('pdo_mysql extension is not installed');
  216. }
  217. if (strpos($this->_config['host'], '/')!==false) {
  218. $this->_config['unix_socket'] = $this->_config['host'];
  219. unset($this->_config['host']);
  220. } else if (strpos($this->_config['host'], ':')!==false) {
  221. list($this->_config['host'], $this->_config['port']) = explode(':', $this->_config['host']);
  222. }
  223. $this->_debugTimer();
  224. parent::_connect();
  225. $this->_debugStat(self::DEBUG_CONNECT, '');
  226. /** @link http://bugs.mysql.com/bug.php?id=18551 */
  227. $this->_connection->query("SET SQL_MODE=''");
  228. if (!$this->_connectionFlagsSet) {
  229. $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
  230. $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
  231. $this->_connectionFlagsSet = true;
  232. }
  233. }
  234. /**
  235. * Run RAW Query
  236. *
  237. * @param string $sql
  238. * @return Zend_Db_Statement_Interface
  239. */
  240. public function raw_query($sql)
  241. {
  242. $lostConnectionMessage = 'SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server during query';
  243. $tries = 0;
  244. do {
  245. $retry = false;
  246. try {
  247. $result = $this->getConnection()->query($sql);
  248. } catch (PDOException $e) {
  249. if ($tries < 10 && $e->getMessage() == $lostConnectionMessage) {
  250. $retry = true;
  251. $tries++;
  252. } else {
  253. throw $e;
  254. }
  255. }
  256. } while ($retry);
  257. return $result;
  258. }
  259. /**
  260. * Run RAW query and Fetch First row
  261. *
  262. * @param string $sql
  263. * @param string|int $field
  264. * @return mixed
  265. */
  266. public function raw_fetchRow($sql, $field=null)
  267. {
  268. if (!$result = $this->raw_query($sql)) {
  269. return false;
  270. }
  271. if (!$row = $result->fetch(PDO::FETCH_ASSOC)) {
  272. return false;
  273. }
  274. if (empty($field)) {
  275. return $row;
  276. } else {
  277. return isset($row[$field]) ? $row[$field] : false;
  278. }
  279. }
  280. /**
  281. * Special handling for PDO query().
  282. * All bind parameter names must begin with ':'
  283. *
  284. * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
  285. * @param array $bind An array of data to bind to the placeholders.
  286. * @return Zend_Db_Pdo_Statement
  287. * @throws Zend_Db_Adapter_Exception To re-throw PDOException.
  288. */
  289. public function query($sql, $bind = array())
  290. {
  291. $this->_debugTimer();
  292. try {
  293. $sql = (string)$sql;
  294. if (strpos($sql, ':') !== false || strpos($sql, '?') !== false) {
  295. $this->_bindParams = $bind;
  296. $sql = preg_replace_callback('#(([\'"])((\\2)|((.*?[^\\\\])\\2)))#',
  297. array($this, 'proccessBindCallback'),
  298. $sql
  299. );
  300. Varien_Exception::processPcreError();
  301. $bind = $this->_bindParams;
  302. }
  303. $result = parent::query($sql, $bind);
  304. }
  305. catch (Exception $e) {
  306. $this->_debugStat(self::DEBUG_QUERY, $sql, $bind);
  307. $this->_debugException($e);
  308. }
  309. $this->_debugStat(self::DEBUG_QUERY, $sql, $bind, $result);
  310. return $result;
  311. }
  312. /**
  313. * Callback function for prepare Query Bind RegExp
  314. *
  315. * @param array $matches
  316. * @return string
  317. */
  318. public function proccessBindCallback($matches)
  319. {
  320. if (isset($matches[6]) && (
  321. strpos($matches[6], "'") !== false ||
  322. strpos($matches[6], ":") !== false ||
  323. strpos($matches[6], "?") !== false)) {
  324. $bindName = ':_mage_bind_var_' . ( ++ $this->_bindIncrement );
  325. $this->_bindParams[$bindName] = $this->_unQuote($matches[6]);
  326. return ' ' . $bindName;
  327. }
  328. return $matches[0];
  329. }
  330. /**
  331. * Unquote raw string (use for auto-bind)
  332. *
  333. * @param string $string
  334. * @return string
  335. */
  336. protected function _unQuote($string)
  337. {
  338. $translate = array(
  339. "\\000" => "\000",
  340. "\\n" => "\n",
  341. "\\r" => "\r",
  342. "\\\\" => "\\",
  343. "\'" => "'",
  344. "\\\"" => "\"",
  345. "\\032" => "\032"
  346. );
  347. return strtr($string, $translate);
  348. }
  349. /**
  350. * Run Multi Query
  351. *
  352. * @param string $sql
  353. * @return array
  354. */
  355. public function multi_query($sql)
  356. {
  357. ##$result = $this->raw_query($sql);
  358. #$this->beginTransaction();
  359. try {
  360. $stmts = $this->_splitMultiQuery($sql);
  361. $result = array();
  362. foreach ($stmts as $stmt) {
  363. $result[] = $this->raw_query($stmt);
  364. }
  365. #$this->commit();
  366. } catch (Exception $e) {
  367. #$this->rollback();
  368. throw $e;
  369. }
  370. $this->resetDdlCache();
  371. return $result;
  372. }
  373. /**
  374. * Split multi statement query
  375. *
  376. * @param $sql string
  377. * @return array
  378. */
  379. protected function _splitMultiQuery($sql)
  380. {
  381. $parts = preg_split('#(;|\'|"|\\\\|//|--|\n|/\*|\*/)#',
  382. $sql,
  383. null,
  384. PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
  385. );
  386. $q = false;
  387. $c = false;
  388. $stmts = array();
  389. $s = '';
  390. foreach ($parts as $i=>$part) {
  391. // strings
  392. if (($part==="'" || $part==='"') && ($i===0 || $parts[$i-1]!=='\\')) {
  393. if ($q===false) {
  394. $q = $part;
  395. } else if ($q===$part) {
  396. $q = false;
  397. }
  398. }
  399. // single line comments
  400. if (($part==='//' || $part==='--') && ($i===0 || $parts[$i-1]==="\n")) {
  401. $c = $part;
  402. } else if ($part==="\n" && ($c==='//' || $c==='--')) {
  403. $c = false;
  404. }
  405. // multi line comments
  406. if ($part==='/*' && $c===false) {
  407. $c = '/*';
  408. } else if ($part==='*/' && $c==='/*') {
  409. $c = false;
  410. }
  411. // statements
  412. if ($part===';' && $q===false && $c===false) {
  413. if (trim($s)!=='') {
  414. $stmts[] = trim($s);
  415. $s = '';
  416. }
  417. } else {
  418. $s .= $part;
  419. }
  420. }
  421. if (trim($s)!=='') {
  422. $stmts[] = trim($s);
  423. }
  424. return $stmts;
  425. }
  426. /**
  427. * Delete foreign key if it exist
  428. *
  429. * @param string $tableName
  430. * @param string $foreignKey
  431. * @param string $shemaName
  432. * @return mixed
  433. */
  434. public function dropForeignKey($tableName, $foreignKey, $schemaName = null)
  435. {
  436. $foreignKeys = $this->getForeignKeys($tableName, $schemaName);
  437. if (isset($foreignKeys[strtoupper($foreignKey)])) {
  438. $sql = sprintf('ALTER TABLE %s DROP FOREIGN KEY %s',
  439. $this->quoteIdentifier($this->_getTableName($tableName, $schemaName)),
  440. $this->quoteIdentifier($foreignKeys[strtoupper($foreignKey)]['FK_NAME']));
  441. $this->resetDdlCache($tableName, $schemaName);
  442. return $this->raw_query($sql);
  443. }
  444. return true;
  445. }
  446. /**
  447. * Delete index from a table if it exist
  448. *
  449. * @param string $tableName
  450. * @param string $keyName
  451. * @param string $shemaName
  452. * @return bool
  453. */
  454. public function dropKey($tableName, $keyName, $shemaName = null)
  455. {
  456. $indexList = $this->getIndexList($tableName, $shemaName);
  457. $keyName = strtoupper($keyName);
  458. if (!isset($indexList[$keyName])) {
  459. return true;
  460. }
  461. if ($keyName == 'PRIMARY') {
  462. $cond = 'DROP PRIMARY KEY';
  463. }
  464. else {
  465. $cond = sprintf('DROP KEY %s', $this->quoteIdentifier($indexList[$keyName]['KEY_NAME']));
  466. }
  467. $sql = sprintf('ALTER TABLE %s %s',
  468. $this->quoteIdentifier($this->_getTableName($tableName, $shemaName)),
  469. $cond);
  470. $this->resetDdlCache($tableName, $shemaName);
  471. return $this->raw_query($sql);
  472. }
  473. /**
  474. * Prepare table before add constraint foreign key
  475. *
  476. * @param string $tableName
  477. * @param string $columnName
  478. * @param string $refTableName
  479. * @param string $refColumnName
  480. * @param string $onDelete
  481. * @return Varien_Db_Adapter_Pdo_Mysql
  482. */
  483. public function purgeOrphanRecords($tableName, $columnName, $refTableName, $refColumnName, $onDelete = 'cascade')
  484. {
  485. if (strtoupper($onDelete) == 'CASCADE'
  486. || strtoupper($onDelete) == 'RESTRICT') {
  487. $sql = "DELETE `p`.* FROM `{$tableName}` AS `p`"
  488. . " LEFT JOIN `{$refTableName}` AS `r`"
  489. . " ON `p`.`{$columnName}` = `r`.`{$refColumnName}`"
  490. . " WHERE `r`.`{$refColumnName}` IS NULL";
  491. $this->raw_query($sql);
  492. }
  493. else if (strtoupper($onDelete) == 'SET NULL') {
  494. $sql = "UPDATE `{$tableName}` AS `p`"
  495. . " LEFT JOIN `{$refTableName}` AS `r`"
  496. . " ON `p`.`{$columnName}` = `r`.`{$refColumnName}`"
  497. . " SET `p`.`{$columnName}`=NULL"
  498. . " WHERE `r`.`{$refColumnName}` IS NULL";
  499. $this->raw_query($sql);
  500. }
  501. return $this;
  502. }
  503. /**
  504. * Add foreign key to table. If FK with same name exist - it will be deleted
  505. *
  506. * @param string $fkName foreign key name
  507. * @param string $tableName main table name
  508. * @param string $keyName main table field name
  509. * @param string $refTableName refered table name
  510. * @param string $refKeyName refered table field name
  511. * @param string $onUpdate on update statement
  512. * @param string $onDelete on delete statement
  513. * @param bool $purge
  514. * @return mixed
  515. */
  516. public function addConstraint($fkName, $tableName, $columnName,
  517. $refTableName, $refColumnName, $onDelete = 'cascade', $onUpdate = 'cascade', $purge = false)
  518. {
  519. if (substr($fkName, 0, 3) != 'FK_') {
  520. $fkName = 'FK_' . $fkName;
  521. }
  522. $this->dropForeignKey($tableName, $fkName);
  523. if ($purge) {
  524. $this->purgeOrphanRecords($tableName, $columnName, $refTableName, $refColumnName, $onDelete);
  525. }
  526. $sql = 'ALTER TABLE `'.$tableName.'` ADD CONSTRAINT `'.$fkName.'`'
  527. . ' FOREIGN KEY (`'.$columnName.'`) REFERENCES `'.$refTableName.'` (`'.$refColumnName.'`)';
  528. if (!is_null($onDelete)) {
  529. $sql .= ' ON DELETE ' . strtoupper($onDelete);
  530. }
  531. if (!is_null($onUpdate)) {
  532. $sql .= ' ON UPDATE ' . strtoupper($onUpdate);
  533. }
  534. $this->resetDdlCache($tableName);
  535. return $this->raw_query($sql);
  536. }
  537. /**
  538. * Check table column exist
  539. *
  540. * @param string $tableName
  541. * @param string $columnName
  542. * @param string $schemaName
  543. * @return bool
  544. */
  545. public function tableColumnExists($tableName, $columnName, $schemaName = null)
  546. {
  547. $describe = $this->describeTable($tableName, $schemaName);
  548. foreach ($describe as $column) {
  549. if ($column['COLUMN_NAME'] == $columnName) {
  550. return true;
  551. }
  552. }
  553. return false;
  554. }
  555. /**
  556. * Add new column to table
  557. *
  558. * @param string $tableName
  559. * @param string $columnName
  560. * @param string $definition
  561. * @return bool
  562. */
  563. public function addColumn($tableName, $columnName, $definition)
  564. {
  565. if ($this->tableColumnExists($tableName, $columnName)) {
  566. return true;
  567. }
  568. $sql = sprintf('ALTER TABLE %s ADD COLUMN %s %s',
  569. $this->quoteIdentifier($tableName),
  570. $this->quoteIdentifier($columnName),
  571. $definition
  572. );
  573. $result = $this->raw_query($sql);
  574. $this->resetDdlCache($tableName);
  575. return $result;
  576. }
  577. /**
  578. * Delete table column
  579. *
  580. * @param string $tableName
  581. * @param string $columnName
  582. * @param string $shemaName
  583. * @return bool
  584. */
  585. public function dropColumn($tableName, $columnName, $shemaName = null)
  586. {
  587. if (!$this->tableColumnExists($tableName, $columnName, $shemaName)) {
  588. return true;
  589. }
  590. $alterDrop = array();
  591. $foreignKeys = $this->getForeignKeys($tableName, $shemaName);
  592. foreach ($foreignKeys as $fkProp) {
  593. if ($fkProp['COLUMN_NAME'] == $columnName) {
  594. $alterDrop[] = sprintf('DROP FOREIGN KEY %s', $this->quoteIdentifier($fkProp['FK_NAME']));
  595. }
  596. }
  597. $alterDrop[] = sprintf('DROP COLUMN %s', $this->quoteIdentifier($columnName));
  598. $sql = sprintf('ALTER TABLE %s %s',
  599. $this->quoteIdentifier($this->_getTableName($tableName, $shemaName)),
  600. join(', ', $alterDrop));
  601. $this->resetDdlCache($tableName, $shemaName);
  602. return $this->raw_query($sql);
  603. }
  604. /**
  605. * Change column
  606. *
  607. * @param string $tableName
  608. * @param string $oldColumnName
  609. * @param string $newColumnName
  610. * @param string $definition
  611. * @param bool $showStatus
  612. *
  613. * @return mixed
  614. */
  615. public function changeColumn($tableName, $oldColumnName, $newColumnName, $definition, $showStatus = false)
  616. {
  617. if (!$this->tableColumnExists($tableName, $oldColumnName)) {
  618. throw new Exception(sprintf('Column "%s" does not exists on table "%s"', $oldColumnName, $tableName));
  619. }
  620. $sql = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s %s',
  621. $this->quoteIdentifier($tableName),
  622. $this->quoteIdentifier($oldColumnName),
  623. $this->quoteIdentifier($newColumnName),
  624. $definition);
  625. $result = $this->raw_query($sql);
  626. if ($showStatus) {
  627. $this->showTableStatus($tableName);
  628. }
  629. $this->resetDdlCache($tableName);
  630. return $result;
  631. }
  632. /**
  633. * Modify column defination or position
  634. *
  635. * @param string $tableName
  636. * @param string $columnName
  637. * @param string $definition
  638. * @param bool $showStatus
  639. *
  640. * @return mixed
  641. */
  642. public function modifyColumn($tableName, $columnName, $definition, $showStatus = false)
  643. {
  644. if (!$this->tableColumnExists($tableName, $columnName)) {
  645. throw new Exception(sprintf('Column "%s" does not exists on table "%s"', $columnName, $tableName));
  646. }
  647. $sql = sprintf('ALTER TABLE %s MODIFY COLUMN %s %s',
  648. $this->quoteIdentifier($tableName),
  649. $this->quoteIdentifier($columnName),
  650. $definition);
  651. $result = $this->raw_query($sql);
  652. if ($showStatus) {
  653. $this->showTableStatus($tableName);
  654. }
  655. $this->resetDdlCache($tableName);
  656. return $result;
  657. }
  658. /**
  659. * Show table status
  660. *
  661. * @param string $tableName
  662. * @return array
  663. */
  664. public function showTableStatus($tableName)
  665. {
  666. $sql = $this->quoteInto('SHOW TABLE STATUS LIKE ?', $tableName);
  667. return $this->raw_fetchRow($sql);
  668. }
  669. /**
  670. * Retrieve table index key list
  671. *
  672. * @deprecated use getIndexList(
  673. * @param string $tableName
  674. * @param string $schemaName
  675. * @return array
  676. */
  677. public function getKeyList($tableName, $schemaName = null)
  678. {
  679. $keyList = array();
  680. $indexList = $this->getIndexList($tableName, $schemaName);
  681. foreach ($indexList as $indexProp) {
  682. $keyList[$indexProp['KEY_NAME']] = $indexProp['COLUMNS_LIST'];
  683. }
  684. return $keyList;
  685. }
  686. /**
  687. * Retrieve Create Table SQL
  688. *
  689. * @param string $tableName
  690. * @param string $schemaName
  691. * @return string
  692. */
  693. public function getCreateTable($tableName, $schemaName = null)
  694. {
  695. $cacheKey = $this->_getTableName($tableName, $schemaName);
  696. $ddl = $this->loadDdlCache($cacheKey, self::DDL_CREATE);
  697. if ($ddl === false) {
  698. $sql = sprintf('SHOW CREATE TABLE %s', $this->quoteIdentifier($tableName));
  699. $ddl = $this->raw_fetchRow($sql, 'Create Table');
  700. $this->saveDdlCache($cacheKey, self::DDL_CREATE, $ddl);
  701. }
  702. return $ddl;
  703. }
  704. /**
  705. * Retrieve the foreign keys descriptions for a table.
  706. *
  707. * The return value is an associative array keyed by the UPPERCASE foreign key,
  708. * as returned by the RDBMS.
  709. *
  710. * The value of each array element is an associative array
  711. * with the following keys:
  712. *
  713. * FK_NAME => string; original foreign key name
  714. * SCHEMA_NAME => string; name of database or schema
  715. * TABLE_NAME => string;
  716. * COLUMN_NAME => string; column name
  717. * REF_SCHEMA_NAME => string; name of reference database or schema
  718. * REF_TABLE_NAME => string; reference table name
  719. * REF_COLUMN_NAME => string; reference column name
  720. * ON_DELETE => string; action type on delete row
  721. * ON_UPDATE => string; action type on update row
  722. *
  723. * @param string $tableName
  724. * @param string $schemaName
  725. * @return array
  726. */
  727. public function getForeignKeys($tableName, $schemaName = null)
  728. {
  729. $cacheKey = $this->_getTableName($tableName, $schemaName);
  730. $ddl = $this->loadDdlCache($cacheKey, self::DDL_FOREIGN_KEY);
  731. if ($ddl === false) {
  732. $ddl = array();
  733. $createSql = $this->getCreateTable($tableName, $schemaName);
  734. // collect CONSTRAINT
  735. $regExp = '#,\s+CONSTRAINT `([^`]*)` FOREIGN KEY \(`([^`]*)`\) '
  736. . 'REFERENCES (`[^`]*\.)?`([^`]*)` \(`([^`]*)`\)'
  737. . '( ON DELETE (RESTRICT|CASCADE|SET NULL|NO ACTION))?'
  738. . '( ON UPDATE (RESTRICT|CASCADE|SET NULL|NO ACTION))?#';
  739. $matches = array();
  740. preg_match_all($regExp, $createSql, $matches, PREG_SET_ORDER);
  741. foreach ($matches as $match) {
  742. $ddl[strtoupper($match[1])] = array(
  743. 'FK_NAME' => $match[1],
  744. 'SCHEMA_NAME' => $schemaName,
  745. 'TABLE_NAME' => $tableName,
  746. 'COLUMN_NAME' => $match[2],
  747. 'REF_SHEMA_NAME' => isset($match[3]) ? $match[3] : $schemaName,
  748. 'REF_TABLE_NAME' => $match[4],
  749. 'REF_COLUMN_NAME' => $match[5],
  750. 'ON_DELETE' => isset($match[6]) ? $match[7] : '',
  751. 'ON_UPDATE' => isset($match[8]) ? $match[9] : ''
  752. );
  753. }
  754. $this->saveDdlCache($cacheKey, self::DDL_FOREIGN_KEY, $ddl);
  755. }
  756. return $ddl;
  757. }
  758. /**
  759. * Retrieve table index information
  760. *
  761. * The return value is an associative array keyed by the UPPERCASE index key,
  762. * as returned by the RDBMS.
  763. *
  764. * The value of each array element is an associative array
  765. * with the following keys:
  766. *
  767. * SCHEMA_NAME => string; name of database or schema
  768. * TABLE_NAME => string; name of the table
  769. * KEY_NAME => string; the original index name
  770. * COLUMNS_LIST => array; array of index column names
  771. * INDEX_TYPE => string; create index type
  772. * INDEX_METHOD => string; index method using
  773. * type => string; see INDEX_TYPE
  774. * fields => array; see COLUMNS_LIST
  775. *
  776. * @param string $tableName
  777. * @param string $schemaName
  778. * @return array
  779. */
  780. public function getIndexList($tableName, $schemaName = null)
  781. {
  782. $cacheKey = $this->_getTableName($tableName, $schemaName);
  783. $ddl = $this->loadDdlCache($cacheKey, self::DDL_INDEX);
  784. if ($ddl === false) {
  785. $ddl = array();
  786. $sql = sprintf('SHOW INDEX FROM %s',
  787. $this->quoteIdentifier($this->_getTableName($tableName, $schemaName)));
  788. foreach ($this->fetchAll($sql) as $row) {
  789. $fieldKeyName = 'Key_name';
  790. $fieldNonUnique = 'Non_unique';
  791. $fieldColumn = 'Column_name';
  792. $fieldIndexType = 'Index_type';
  793. if ($row[$fieldKeyName] == 'PRIMARY') {
  794. $indexType = 'primary';
  795. }
  796. else if ($row[$fieldNonUnique] == 0) {
  797. $indexType = 'unique';
  798. }
  799. else if ($row[$fieldIndexType] == 'FULLTEXT') {
  800. $indexType = 'fulltext';
  801. }
  802. else {
  803. $indexType = 'index';
  804. }
  805. $upperKeyName = strtoupper($row[$fieldKeyName]);
  806. if (isset($ddl[$upperKeyName])) {
  807. $ddl[$upperKeyName]['fields'][] = $row[$fieldColumn]; // for compatible
  808. $ddl[$upperKeyName]['COLUMNS_LIST'][] = $row[$fieldColumn];
  809. }
  810. else {
  811. $ddl[$upperKeyName] = array(
  812. 'SCHEMA_NAME' => $schemaName,
  813. 'TABLE_NAME' => $tableName,
  814. 'KEY_NAME' => $row[$fieldKeyName],
  815. 'COLUMNS_LIST' => array($row[$fieldColumn]),
  816. 'INDEX_TYPE' => strtoupper($indexType),
  817. 'INDEX_METHOD' => $row[$fieldIndexType],
  818. 'type' => $indexType, // for compatible
  819. 'fields' => array($row[$fieldColumn]) // for compatible
  820. );
  821. }
  822. }
  823. $this->saveDdlCache($cacheKey, self::DDL_INDEX, $ddl);
  824. }
  825. return $ddl;
  826. }
  827. /**
  828. * Add Index Key
  829. *
  830. * @param string $tableName
  831. * @param string $indexName
  832. * @param string|array $fields
  833. * @param string $indexType
  834. * @return
  835. */
  836. public function addKey($tableName, $indexName, $fields, $indexType = 'index')
  837. {
  838. $columns = $this->describeTable($tableName);
  839. $keyList = $this->getKeyList($tableName);
  840. $sql = 'ALTER TABLE '.$this->quoteIdentifier($tableName);
  841. if (isset($keyList[$indexName])) {
  842. $sql .= ' DROP INDEX ' . $this->quoteIdentifier($indexName) . ',';
  843. }
  844. if (!is_array($fields)) {
  845. $fields = array($fields);
  846. }
  847. $fieldSql = array();
  848. foreach ($fields as $field) {
  849. if (!isset($columns[$field])) {
  850. $msg = sprintf('There is no field "%s" that you are trying to create an index on "%s"',
  851. $field, $tableName);
  852. throw new Exception($msg);
  853. }
  854. $fieldSql[] = $this->quoteIdentifier($field);
  855. }
  856. $fieldSql = join(',', $fieldSql);
  857. switch (strtolower($indexType)) {
  858. case 'primary':
  859. $condition = 'PRIMARY KEY';
  860. break;
  861. case 'unique':
  862. $condition = 'UNIQUE ' . $this->quoteIdentifier($indexName);
  863. break;
  864. case 'fulltext':
  865. $condition = 'FULLTEXT ' . $this->quoteIdentifier($indexName);
  866. break;
  867. default:
  868. $condition = 'INDEX ' . $this->quoteIdentifier($indexName);
  869. break;
  870. }
  871. $sql .= ' ADD ' . $condition . ' (' . $fieldSql . ')';
  872. $cycle = true;
  873. while ($cycle === true) {
  874. try {
  875. $result = $this->raw_query($sql);
  876. $cycle = false;
  877. }
  878. catch (PDOException $e) {
  879. if (in_array(strtolower($indexType), array('primary', 'unique'))) {
  880. $match = array();
  881. if (preg_match('#SQLSTATE\[23000\]: [^:]+: 1062[^\']+\'([\d-\.]+)\'#', $e->getMessage(), $match)) {
  882. $ids = explode('-', $match[1]);
  883. $this->_removeDuplicateEntry($tableName, $fields, $ids);
  884. continue;
  885. }
  886. }
  887. throw $e;
  888. }
  889. catch (Exception $e) {
  890. throw $e;
  891. }
  892. }
  893. $this->resetDdlCache($tableName);
  894. return $result;
  895. }
  896. /**
  897. * Remove duplicate entry for create key
  898. *
  899. * @param string $table
  900. * @param array $fields
  901. * @param array $ids
  902. * @return Varien_Db_Adapter_Pdo_Mysql
  903. */
  904. protected function _removeDuplicateEntry($table, $fields, $ids)
  905. {
  906. $where = array();
  907. $i = 0;
  908. foreach ($fields as $field) {
  909. $where[] = $this->quoteInto($field . '=?', $ids[$i]);
  910. $i ++;
  911. }
  912. if (!$where) {
  913. return $this;
  914. }
  915. $whereCond = join(' AND ', $where);
  916. $sql = sprintf('SELECT COUNT(*) as `cnt` FROM `%s` WHERE %s', $table, $whereCond);
  917. if ($cnt = $this->raw_fetchRow($sql, 'cnt')) {
  918. $sql = sprintf('DELETE FROM `%s` WHERE %s LIMIT %d',
  919. $table,
  920. $whereCond,
  921. $cnt - 1
  922. );
  923. $this->raw_query($sql);
  924. }
  925. return $this;
  926. }
  927. /**
  928. * Creates and returns a new Zend_Db_Select object for this adapter.
  929. *
  930. * @return Varien_Db_Select
  931. */
  932. public function select()
  933. {
  934. return new Varien_Db_Select($this);
  935. }
  936. /**
  937. * Start debug timer
  938. *
  939. * @return Varien_Db_Adapter_Pdo_Mysql
  940. */
  941. protected function _debugTimer()
  942. {
  943. if ($this->_debug) {
  944. $this->_debugTimer = microtime(true);
  945. }
  946. return $this;
  947. }
  948. /**
  949. * Logging debug information
  950. *
  951. * @param int $type
  952. * @param string $sql
  953. * @param array $bind
  954. * @param Zend_Db_Statement_Pdo $result
  955. * @return Varien_Db_Adapter_Pdo_Mysql
  956. */
  957. protected function _debugStat($type, $sql, $bind = array(), $result = null)
  958. {
  959. if (!$this->_debug) {
  960. return $this;
  961. }
  962. $code = '## ' . getmypid() . ' ## ';
  963. $nl = "\n";
  964. $time = sprintf('%.4f', microtime(true) - $this->_debugTimer);
  965. if (!$this->_logAllQueries && $time < $this->_logQueryTime) {
  966. return $this;
  967. }
  968. switch ($type) {
  969. case self::DEBUG_CONNECT:
  970. $code .= 'CONNECT' . $nl;
  971. break;
  972. case self::DEBUG_TRANSACTION:
  973. $code .= 'TRANSACTION ' . $sql . $nl;
  974. break;
  975. case self::DEBUG_QUERY:
  976. $code .= 'QUERY' . $nl;
  977. $code .= 'SQL: ' . $sql . $nl;
  978. if ($bind) {
  979. $code .= 'BIND: ' . print_r($bind, true) . $nl;
  980. }
  981. if ($result instanceof Zend_Db_Statement_Pdo) {
  982. $code .= 'AFF: ' . $result->rowCount() . $nl;
  983. }
  984. break;
  985. }
  986. $code .= 'TIME: ' . $time . $nl;
  987. if ($this->_logCallStack) {
  988. $code .= 'TRACE: ' . Varien_Debug::backtrace(true, false) . $nl;
  989. }
  990. $code .= $nl;
  991. $this->_debugWriteToFile($code);
  992. return $this;
  993. }
  994. /**
  995. * Write exception and thow
  996. *
  997. * @param Exception $e
  998. * @throws Exception
  999. */
  1000. protected function _debugException(Exception $e)
  1001. {
  1002. if (!$this->_debug) {
  1003. throw $e;
  1004. }
  1005. $nl = "\n";
  1006. $code = 'EXCEPTION ' . $nl . $e . $nl . $nl;
  1007. $this->_debugWriteToFile($code);
  1008. throw $e;
  1009. }
  1010. /**
  1011. * Debug write to file process
  1012. *
  1013. * @param string $str
  1014. */
  1015. protected function _debugWriteToFile($str)
  1016. {
  1017. if (!$this->_debugIoAdapter) {
  1018. $this->_debugIoAdapter = new Varien_Io_File();
  1019. $dir = $this->_debugIoAdapter->dirname($this->_debugFile);
  1020. $this->_debugIoAdapter->checkAndCreateFolder($dir);
  1021. $this->_debugIoAdapter->open(array('path' => $dir));
  1022. $this->_debugFile = basename($this->_debugFile);
  1023. }
  1024. $this->_debugIoAdapter->streamOpen($this->_debugFile, 'a');
  1025. $this->_debugIoAdapter->streamLock();
  1026. $this->_debugIoAdapter->streamWrite($str);
  1027. $this->_debugIoAdapter->streamUnlock();
  1028. $this->_debugIoAdapter->streamClose();
  1029. }
  1030. /**
  1031. * Quotes a value and places into a piece of text at a placeholder.
  1032. *
  1033. * Method revrited for handle empty arrays in value param
  1034. *
  1035. * @param string $text The text with a placeholder.
  1036. * @param mixed $value The value to quote.
  1037. * @param string $type OPTIONAL SQL datatype
  1038. * @param integer $count OPTIONAL count of placeholders to replace
  1039. * @return string An SQL-safe quoted value placed into the orignal text.
  1040. */
  1041. public function quoteInto($text, $value, $type = null, $count = null)
  1042. {
  1043. if (is_array($value) && empty($value)) {
  1044. $value = new Zend_Db_Expr('NULL');
  1045. }
  1046. return parent::quoteInto($text, $value, $type, $count);
  1047. }
  1048. /**
  1049. * Retrieve ddl cache name
  1050. *
  1051. * @param string $tableName
  1052. * @param string $schemaName
  1053. */
  1054. protected function _getTableName($tableName, $schemaName = null)
  1055. {
  1056. return ($schemaName ? $schemaName . '.' : '') . $tableName;
  1057. }
  1058. /**
  1059. * Retrieve Id for cache
  1060. *
  1061. * @param string $tableKey
  1062. * @param int $ddlType
  1063. * @return string
  1064. */
  1065. protected function _getCacheId($tableKey, $ddlType)
  1066. {
  1067. return sprintf('%s_%s_%s', self::DDL_CACHE_PREFIX, $tableKey, $ddlType);
  1068. }
  1069. /**
  1070. * Load DDL data from cache
  1071. * Return false if cache does not exists
  1072. *
  1073. * @param string $tableCacheKey the table cache key
  1074. * @param int $ddlType the DDL constant
  1075. * @return string|array|int|false
  1076. */
  1077. public function loadDdlCache($tableCacheKey, $ddlType)
  1078. {
  1079. if (!$this->_isDdlCacheAllowed) {
  1080. return false;
  1081. }
  1082. if (isset($this->_ddlCache[$ddlType][$tableCacheKey])) {
  1083. return $this->_ddlCache[$ddlType][$tableCacheKey];
  1084. }
  1085. if ($this->_cacheAdapter instanceof Zend_Cache_Core) {
  1086. $cacheId = $this->_getCacheId($tableCacheKey, $ddlType);
  1087. $data = $this->_cacheAdapter->load($cacheId);
  1088. if ($data !== false) {
  1089. $data = unserialize($data);
  1090. $this->_ddlCache[$ddlType][$tableCacheKey] = $data;
  1091. }
  1092. return $data;
  1093. }
  1094. return false;
  1095. }
  1096. /**
  1097. * Save DDL data into cache
  1098. *
  1099. * @param string $tableCacheKey
  1100. * @param int $ddlType
  1101. * @return Varien_Db_Adapter_Pdo_Mysql
  1102. */
  1103. public function saveDdlCache($tableCacheKey, $ddlType, $data)
  1104. {
  1105. if (!$this->_isDdlCacheAllowed) {
  1106. return $this;
  1107. }
  1108. $this->_ddlCache[$ddlType][$tableCacheKey] = $data;
  1109. if ($this->_cacheAdapter instanceof Zend_Cache_Core) {
  1110. $cacheId = $this->_getCacheId($tableCacheKey, $ddlType);
  1111. $data = serialize($data);
  1112. $this->_cacheAdapter->save($data, $cacheId, array(self::DDL_CACHE_TAG));
  1113. }
  1114. return $this;
  1115. }
  1116. /**
  1117. * Reset cached DDL data from cache
  1118. * if table name is null - reset all cached DDL data
  1119. *
  1120. * @param string $tableName
  1121. * @param string $schemaName OPTIONAL
  1122. * @return Varien_Db_Adapter_Pdo_Mysql
  1123. */
  1124. public function resetDdlCache($tableName = null, $schemaName = null)
  1125. {
  1126. if (!$this->_isDdlCacheAllowed) {
  1127. return $this;
  1128. }
  1129. if (is_null($tableName)) {
  1130. $this->_ddlCache = array();
  1131. if ($this->_cacheAdapter instanceof Zend_Cache_Core) {
  1132. $this->_cacheAdapter->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array(self::DDL_CACHE_TAG));
  1133. }
  1134. } else {
  1135. $cacheKey = $this->_getTableName($tableName, $schemaName);
  1136. $ddlTypes = array(self::DDL_DESCRIBE, self::DDL_CREATE, self::DDL_INDEX, self::DDL_FOREIGN_KEY);
  1137. foreach ($ddlTypes as $ddlType) {
  1138. unset($this->_ddlCache[$ddlType][$cacheKey]);
  1139. }
  1140. if ($this->_cacheAdapter instanceof Zend_Cache_Core) {
  1141. foreach ($ddlTypes as $ddlType) {
  1142. $cacheId = $this->_getCacheId($cacheKey, $ddlType);
  1143. $this->_cacheAdapter->remove($cacheId);
  1144. }
  1145. }
  1146. }
  1147. return $this;
  1148. }
  1149. /**
  1150. * Disallow DDL caching
  1151. * @return Varien_Db_Adapter_Pdo_Mysql
  1152. */
  1153. public function disallowDdlCache()
  1154. {
  1155. $this->_isDdlCacheAllowed = false;
  1156. return $this;
  1157. }
  1158. /**
  1159. * Allow DDL caching
  1160. * @return Varien_Db_Adapter_Pdo_Mysql
  1161. */
  1162. public function allowDdlCache()
  1163. {
  1164. $this->_isDdlCacheAllowed = true;
  1165. return $this;
  1166. }
  1167. /**
  1168. * Returns the column descriptions for a table.
  1169. *
  1170. * The return value is an associative array keyed by the column name,
  1171. * as returned by the RDBMS.
  1172. *
  1173. * The value of each array element is an associative array
  1174. * with the following keys:
  1175. *
  1176. * SCHEMA_NAME => string; name of database or schema
  1177. * TABLE_NAME => string;
  1178. * COLUMN_NAME => string; column name
  1179. * COLUMN_POSITION => number; ordinal position of column in table
  1180. * DATA_TYPE => string; SQL datatype name of column
  1181. * DEFAULT => string; default expression of column, null if none
  1182. * NULLABLE => boolean; true if column can have nulls
  1183. * LENGTH => number; length of CHAR/VARCHAR
  1184. * SCALE => number; scale of NUMERIC/DECIMAL
  1185. * PRECISION => number; precision of NUMERIC/DECIMAL
  1186. * UNSIGNED => boolean; unsigned property of an integer type
  1187. * PRIMARY => boolean; true if column is part of the primary key
  1188. * PRIMARY_POSITION => integer; position of column in primary key
  1189. * IDENTITY => integer; true if column is auto-generated with unique values
  1190. *
  1191. * @param string $tableName
  1192. * @param string $schemaName OPTIONAL
  1193. * @return array
  1194. */
  1195. public function describeTable($tableName, $schemaName = null)
  1196. {
  1197. $cacheKey = $this->_getTableName($tableName, $schemaName);
  1198. $ddl = $this->loadDdlCache($cacheKey, self::DDL_DESCRIBE);
  1199. if ($ddl === false) {
  1200. $ddl = parent::describeTable($tableName, $schemaName);
  1201. $this->saveDdlCache($cacheKey, self::DDL_DESCRIBE, $ddl);
  1202. }
  1203. return $ddl;
  1204. }
  1205. /**
  1206. * Truncate table
  1207. *
  1208. * @param string $tableName
  1209. * @param string $schemaName
  1210. * @return Varien_Db_Adapter_Pdo_Mysql
  1211. */
  1212. public function truncate($tableName, $schemaName = null)
  1213. {
  1214. $tableName = $this->_getTableName($tableName, $schemaName);
  1215. $sql = sprintf('TRUNCATE %s', $this->quoteIdentifier($tableName));
  1216. $this->raw_query($sql);
  1217. return $this;
  1218. }
  1219. /**
  1220. * Change table storage engine
  1221. *
  1222. * @param string $tableName
  1223. * @param string $engine
  1224. * @param string $schemaName
  1225. * @return mixed
  1226. */
  1227. public function changeTableEngine($tableName, $engine, $schemaName = null)
  1228. {
  1229. $sql = sprintf('ALTER TABLE %s ENGINE=%s',
  1230. $this->quoteIdentifier($this->_getTableName($tableName, $schemaName)),
  1231. $engine);
  1232. return $this->raw_query($sql);
  1233. }
  1234. /**
  1235. * Inserts a table row with specified data.
  1236. *
  1237. * @param mixed $table The table to insert data into.
  1238. * @param array $data Column-value pairs or array of column-value pairs.
  1239. * @param array $fields update fields pairs or values
  1240. * @return int The number of affected rows.
  1241. */
  1242. public function insertOnDuplicate($table, array $data, array $fields = array())
  1243. {
  1244. // extract and quote col names from the array keys
  1245. $row = reset($data); // get first element from data array
  1246. $bind = array(); // SQL bind array
  1247. $values = array();
  1248. if (is_array($row)) { // Array of column-value pairs
  1249. $cols = array_keys($row);
  1250. foreach ($data as $row) {
  1251. $line = array();
  1252. if (array_diff($cols, array_keys($row))) {
  1253. throw new Varien_Exception('Invalid data for insert');
  1254. }
  1255. foreach ($row as $val) {
  1256. if ($val instanceof Zend_Db_Expr) {
  1257. $line[] = $val->__toString();
  1258. } else {
  1259. $line[] = '?';
  1260. $bind[] = $val;
  1261. }
  1262. }
  1263. $values[] = sprintf('(%s)', join(',', $line));
  1264. }
  1265. unset($row);
  1266. } else { // Column-value pairs
  1267. $cols = array_keys($data);
  1268. $line = array();
  1269. foreach ($data as $val) {
  1270. if ($val instanceof Zend_Db_Expr) {
  1271. $line[] = $val->__toString();
  1272. } else {
  1273. $line[] = '?';
  1274. $bind[] = $val;
  1275. }
  1276. }
  1277. $values[] = sprintf('(%s)', join(',', $line));
  1278. }
  1279. $updateFields = array();
  1280. if (empty($fields)) {
  1281. $fields = $cols;
  1282. }
  1283. // quote column names
  1284. $cols = array_map(array($this, 'quoteIdentifier'), $cols);
  1285. // prepare ON DUPLICATE KEY conditions
  1286. foreach ($fields as $k => $v) {
  1287. $field = $value = null;
  1288. if (!is_numeric($k)) {
  1289. $field = $this->quoteIdentifier($k);
  1290. if ($v instanceof Zend_Db_Expr) {
  1291. $value = $v->__toString();
  1292. } else if (is_string($v)) {
  1293. $value = 'VALUES('.$this->quoteIdentifier($v).')';
  1294. } else if (is_numeric($v)) {
  1295. $value = $this->quoteInto('?', $v);
  1296. }
  1297. } else if (is_string($v)) {
  1298. $field = $this->quoteIdentifier($v);
  1299. $value = 'VALUES('.$field.')';
  1300. }
  1301. if ($field && $value) {
  1302. $updateFields[] = "{$field}={$value}";
  1303. }
  1304. }
  1305. // build the statement
  1306. $sql = "INSERT INTO "
  1307. . $this->quoteIdentifier($table, true)
  1308. . ' (' . implode(', ', $cols) . ') '
  1309. . 'VALUES ' . implode(', ', $values);
  1310. if ($updateFields) {
  1311. $sql .= " ON DUPLICATE KEY UPDATE " . join(', ', $updateFields);
  1312. }
  1313. // execute the statement and return the number of affected rows
  1314. $stmt = $this->query($sql, array_values($bind));
  1315. $result = $stmt->rowCount();
  1316. return $result;
  1317. }
  1318. /**
  1319. * Inserts a table multiply rows with specified data.
  1320. *
  1321. * @param mixed $table The table to insert data into.
  1322. * @param array $data Column-value pairs or array of Column-value pairs.
  1323. * @return int The number of affected rows.
  1324. */
  1325. public function insertMultiple($table, array $data)
  1326. {
  1327. $row = reset($data);
  1328. // support insert syntaxes
  1329. if (!is_array($row)) {
  1330. return $this->insert($table, $data);
  1331. }
  1332. // validate data array
  1333. $cols = array_keys($row);
  1334. $insertArray = array();
  1335. foreach ($data as $row) {
  1336. $line = array();
  1337. if (array_diff($cols, array_keys($row))) {
  1338. throw new Varien_Exception('Invalid data for insert');
  1339. }
  1340. foreach ($cols as $field) {
  1341. $line[] = $row[$field];
  1342. }
  1343. $insertArray[] = $line;
  1344. }
  1345. unset($row);
  1346. return $this->insertArray($table, $cols, $insertArray);
  1347. }
  1348. /**
  1349. * Insert array to table based on columns definition
  1350. *
  1351. * @param string $table
  1352. * @param array $columns
  1353. * @param array $data
  1354. * @return int
  1355. */
  1356. public function insertArray($table, array $columns, array $data)
  1357. {
  1358. $vals = array();
  1359. $bind = array();
  1360. $columnsCount = count($columns);
  1361. foreach ($data as $row) {
  1362. if ($columnsCount != count($row)) {
  1363. throw new Varien_Exception('Invalid data for insert');
  1364. }
  1365. $line = array();
  1366. if ($columnsCount == 1) {
  1367. if ($row instanceof Zend_Db_Expr) {
  1368. $line = $row->__toString();
  1369. } else {
  1370. $line = '?';
  1371. $bind[] = $row;
  1372. }
  1373. $vals[] = sprintf('(%s)', $line);
  1374. } else {
  1375. foreach ($row as $value) {
  1376. if ($value instanceof Zend_Db_Expr) {
  1377. $line[] = $value->__toString();
  1378. }
  1379. else {
  1380. $line[] = '?';
  1381. $bind[] = $value;
  1382. }
  1383. }
  1384. $vals[] = sprintf('(%s)', join(',', $line));
  1385. }
  1386. }
  1387. // build the statement
  1388. $columns = array_map(array($this, 'quoteIdentifier'), $columns);
  1389. $sql = sprintf("INSERT INTO %s (%s) VALUES%s",
  1390. $this->quoteIdentifier($table, true),
  1391. implode(',', $columns), implode(', ', $vals));
  1392. // execute the statement and return the number of affected rows
  1393. $stmt = $this->query($sql, $bind);
  1394. $result = $stmt->rowCount();
  1395. return $result;
  1396. }
  1397. /**
  1398. * Set cache adapter
  1399. *
  1400. * @param Zend_Cache_Backend_Interface $adapter
  1401. * @return Varien_Db_Adapter_Pdo_Mysql
  1402. */
  1403. public function setCacheAdapter($adapter)
  1404. {
  1405. $this->_cacheAdapter = $adapter;
  1406. return $this;
  1407. }
  1408. /**
  1409. * Return DDL Table object
  1410. *
  1411. * @param string $tableName the table name
  1412. * @return Varien_Db_Ddl_Table
  1413. */
  1414. public function newTable($tableName = null)
  1415. {
  1416. $table = new Varien_Db_Ddl_Table();
  1417. if (!is_null($tableName)) {
  1418. $table->setName($tableName);
  1419. }
  1420. return $table;
  1421. }
  1422. /**
  1423. * Create table
  1424. *
  1425. * @param Varien_Db_Ddl_Table $table
  1426. * @throws Zend_Db_Exception
  1427. * @return Zend_Db_Pdo_Statement
  1428. */
  1429. public function createTable(Varien_Db_Ddl_Table $table)
  1430. {
  1431. $sqlFragment = array_merge(
  1432. $this->_getColumnsDefinition($table),
  1433. $this->_getIndexesDefinition($table),
  1434. $this->_getForeignKeysDefinition($table)
  1435. );
  1436. $tableOptions = $this->_getOptionsDefination($table);
  1437. $sql = sprintf("CREATE TABLE %s (\n%s\n) %s",
  1438. $this->quoteIdentifier($table->getName()),
  1439. implode(",\n", $sqlFragment),
  1440. implode(" ", $tableOptions));
  1441. return $this->query($sql);
  1442. }
  1443. /**
  1444. * Retrieve columns and primary keys definition array for create table
  1445. *
  1446. * @param Varien_Db_Ddl_Table $table
  1447. * @return array
  1448. */
  1449. protected function _getColumnsDefinition(Varien_Db_Ddl_Table $table)
  1450. {
  1451. $definition = array();
  1452. $primary = array();
  1453. $columns = $table->getColumns();
  1454. if (empty($columns)) {
  1455. throw new Zend_Db_Exception('Table columns are not defined');
  1456. }
  1457. $dataTypes = array(
  1458. Varien_Db_Ddl_Table::TYPE_BOOLEAN => 'bool',
  1459. Varien_Db_Ddl_Table::TYPE_TINYINT => 'tinyint',
  1460. Varien_Db_Ddl_Table::TYPE_SMALLINT => 'smallint',
  1461. Varien_Db_Ddl_Table::TYPE_INTEGER => 'int',
  1462. Varien_Db_Ddl_Table::TYPE_BIGINT => 'bigint',
  1463. Varien_Db_Ddl_Table::TYPE_DOUBLE => 'double',
  1464. Varien_Db_Ddl_Table::TYPE_FLOAT => 'float',
  1465. Varien_Db_Ddl_Table::TYPE_REAL => 'real',
  1466. Varien_Db_Ddl_Table::TYPE_DECIMAL => 'decimal',
  1467. Varien_Db_Ddl_Table::TYPE

Large files files are truncated, but you can click here to view the full file