PageRenderTime 84ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/library/Varien/Db/Adapter/Pdo/Mysql.php

https://bitbucket.org/aukhanev/xdn-wordpress31
PHP | 3586 lines | 3339 code | 47 blank | 200 comment | 34 complexity | 49cec4cf8aa903eb9c4cff6bb26d7934 MD5 | raw file

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

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