PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/common/libraries/plugin/pear/MDB2/Driver/mysql.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 1867 lines | 1189 code | 167 blank | 511 comment | 206 complexity | 7c1524378e4b3bdd8552f72aa3599a91 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0

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

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
  7. // | Stig. S. Bakken, Lukas Smith |
  8. // | All rights reserved. |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
  11. // | API as well as database abstraction for PHP applications. |
  12. // | This LICENSE is in the BSD license style. |
  13. // | |
  14. // | Redistribution and use in source and binary forms, with or without |
  15. // | modification, are permitted provided that the following conditions |
  16. // | are met: |
  17. // | |
  18. // | Redistributions of source code must retain the above copyright |
  19. // | notice, this list of conditions and the following disclaimer. |
  20. // | |
  21. // | Redistributions in binary form must reproduce the above copyright |
  22. // | notice, this list of conditions and the following disclaimer in the |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // | |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission. |
  29. // | |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
  41. // | POSSIBILITY OF SUCH DAMAGE. |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Lukas Smith <smith@pooteeweet.org> |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: mysql.php 137 2009-11-09 13:24:37Z vanpouckesven $
  47. //
  48. /**
  49. * MDB2 MySQL driver
  50. *
  51. * @package MDB2
  52. * @category Database
  53. * @author Lukas Smith <smith@pooteeweet.org>
  54. */
  55. class MDB2_Driver_mysql extends MDB2_Driver_Common
  56. {
  57. // {{{ properties
  58. var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => '\\', 'escape_pattern' => '\\');
  59. var $identifier_quoting = array('start' => '`', 'end' => '`', 'escape' => '`');
  60. var $sql_comments = array(array('start' => '-- ', 'end' => "\n", 'escape' => false),
  61. array('start' => '#', 'end' => "\n", 'escape' => false),
  62. array('start' => '/*', 'end' => '*/', 'escape' => false));
  63. var $server_capabilities_checked = false;
  64. var $start_transaction = false;
  65. var $varchar_max_length = 255;
  66. // }}}
  67. // {{{ constructor
  68. /**
  69. * Constructor
  70. */
  71. function __construct()
  72. {
  73. parent :: __construct();
  74. $this->phptype = 'mysql';
  75. $this->dbsyntax = 'mysql';
  76. $this->supported['sequences'] = 'emulated';
  77. $this->supported['indexes'] = true;
  78. $this->supported['affected_rows'] = true;
  79. $this->supported['transactions'] = false;
  80. $this->supported['savepoints'] = false;
  81. $this->supported['summary_functions'] = true;
  82. $this->supported['order_by_text'] = true;
  83. $this->supported['current_id'] = 'emulated';
  84. $this->supported['limit_queries'] = true;
  85. $this->supported['LOBs'] = true;
  86. $this->supported['replace'] = true;
  87. $this->supported['sub_selects'] = 'emulated';
  88. $this->supported['triggers'] = false;
  89. $this->supported['auto_increment'] = true;
  90. $this->supported['primary_key'] = true;
  91. $this->supported['result_introspection'] = true;
  92. $this->supported['prepared_statements'] = 'emulated';
  93. $this->supported['identifier_quoting'] = true;
  94. $this->supported['pattern_escaping'] = true;
  95. $this->supported['new_link'] = true;
  96. $this->options['DBA_username'] = false;
  97. $this->options['DBA_password'] = false;
  98. $this->options['default_table_type'] = '';
  99. $this->options['max_identifiers_length'] = 64;
  100. $this->_reCheckSupportedOptions();
  101. }
  102. // }}}
  103. // {{{ _reCheckSupportedOptions()
  104. /**
  105. * If the user changes certain options, other capabilities may depend
  106. * on the new settings, so we need to check them (again).
  107. *
  108. * @access private
  109. */
  110. function _reCheckSupportedOptions()
  111. {
  112. $this->supported['transactions'] = $this->options['use_transactions'];
  113. $this->supported['savepoints'] = $this->options['use_transactions'];
  114. if ($this->options['default_table_type'])
  115. {
  116. switch (strtoupper($this->options['default_table_type']))
  117. {
  118. case 'BLACKHOLE' :
  119. case 'MEMORY' :
  120. case 'ARCHIVE' :
  121. case 'CSV' :
  122. case 'HEAP' :
  123. case 'ISAM' :
  124. case 'MERGE' :
  125. case 'MRG_ISAM' :
  126. case 'ISAM' :
  127. case 'MRG_MYISAM' :
  128. case 'MYISAM' :
  129. $this->supported['savepoints'] = false;
  130. $this->supported['transactions'] = false;
  131. $this->warnings[] = $this->options['default_table_type'] . ' is not a supported default table type';
  132. break;
  133. }
  134. }
  135. }
  136. // }}}
  137. // {{{ function setOption($option, $value)
  138. /**
  139. * set the option for the db class
  140. *
  141. * @param string option name
  142. * @param mixed value for the option
  143. *
  144. * @return mixed MDB2_OK or MDB2 Error Object
  145. *
  146. * @access public
  147. */
  148. function setOption($option, $value)
  149. {
  150. $res = parent :: setOption($option, $value);
  151. $this->_reCheckSupportedOptions();
  152. }
  153. // }}}
  154. // {{{ errorInfo()
  155. /**
  156. * This method is used to collect information about an error
  157. *
  158. * @param integer $error
  159. * @return array
  160. * @access public
  161. */
  162. function errorInfo($error = null)
  163. {
  164. if ($this->connection)
  165. {
  166. $native_code = @mysql_errno($this->connection);
  167. $native_msg = @mysql_error($this->connection);
  168. }
  169. else
  170. {
  171. $native_code = @mysql_errno();
  172. $native_msg = @mysql_error();
  173. }
  174. if (is_null($error))
  175. {
  176. static $ecode_map;
  177. if (empty($ecode_map))
  178. {
  179. $ecode_map = array(1000 => MDB2_ERROR_INVALID, //hashchk
  180. 1001 => MDB2_ERROR_INVALID, //isamchk
  181. 1004 => MDB2_ERROR_CANNOT_CREATE, 1005 => MDB2_ERROR_CANNOT_CREATE,
  182. 1006 => MDB2_ERROR_CANNOT_CREATE, 1007 => MDB2_ERROR_ALREADY_EXISTS,
  183. 1008 => MDB2_ERROR_CANNOT_DROP, 1009 => MDB2_ERROR_CANNOT_DROP, 1010 => MDB2_ERROR_CANNOT_DROP,
  184. 1011 => MDB2_ERROR_CANNOT_DELETE, 1022 => MDB2_ERROR_ALREADY_EXISTS,
  185. 1029 => MDB2_ERROR_NOT_FOUND, 1032 => MDB2_ERROR_NOT_FOUND, 1044 => MDB2_ERROR_ACCESS_VIOLATION,
  186. 1045 => MDB2_ERROR_ACCESS_VIOLATION, 1046 => MDB2_ERROR_NODBSELECTED,
  187. 1048 => MDB2_ERROR_CONSTRAINT, 1049 => MDB2_ERROR_NOSUCHDB, 1050 => MDB2_ERROR_ALREADY_EXISTS,
  188. 1051 => MDB2_ERROR_NOSUCHTABLE, 1054 => MDB2_ERROR_NOSUCHFIELD,
  189. 1060 => MDB2_ERROR_ALREADY_EXISTS, 1061 => MDB2_ERROR_ALREADY_EXISTS,
  190. 1062 => MDB2_ERROR_ALREADY_EXISTS, 1064 => MDB2_ERROR_SYNTAX, 1067 => MDB2_ERROR_INVALID,
  191. 1072 => MDB2_ERROR_NOT_FOUND, 1086 => MDB2_ERROR_ALREADY_EXISTS, 1091 => MDB2_ERROR_NOT_FOUND,
  192. 1100 => MDB2_ERROR_NOT_LOCKED, 1109 => MDB2_ERROR_NOT_FOUND, 1125 => MDB2_ERROR_ALREADY_EXISTS,
  193. 1136 => MDB2_ERROR_VALUE_COUNT_ON_ROW, 1138 => MDB2_ERROR_INVALID,
  194. 1142 => MDB2_ERROR_ACCESS_VIOLATION, 1143 => MDB2_ERROR_ACCESS_VIOLATION,
  195. 1146 => MDB2_ERROR_NOSUCHTABLE, 1149 => MDB2_ERROR_SYNTAX, 1169 => MDB2_ERROR_CONSTRAINT,
  196. 1176 => MDB2_ERROR_NOT_FOUND, 1177 => MDB2_ERROR_NOSUCHTABLE, 1213 => MDB2_ERROR_DEADLOCK,
  197. 1216 => MDB2_ERROR_CONSTRAINT, 1217 => MDB2_ERROR_CONSTRAINT,
  198. 1227 => MDB2_ERROR_ACCESS_VIOLATION, 1235 => MDB2_ERROR_CANNOT_CREATE,
  199. 1299 => MDB2_ERROR_INVALID_DATE, 1300 => MDB2_ERROR_INVALID, 1304 => MDB2_ERROR_ALREADY_EXISTS,
  200. 1305 => MDB2_ERROR_NOT_FOUND, 1306 => MDB2_ERROR_CANNOT_DROP, 1307 => MDB2_ERROR_CANNOT_CREATE,
  201. 1334 => MDB2_ERROR_CANNOT_ALTER, 1339 => MDB2_ERROR_NOT_FOUND, 1356 => MDB2_ERROR_INVALID,
  202. 1359 => MDB2_ERROR_ALREADY_EXISTS, 1360 => MDB2_ERROR_NOT_FOUND, 1363 => MDB2_ERROR_NOT_FOUND,
  203. 1365 => MDB2_ERROR_DIVZERO, 1451 => MDB2_ERROR_CONSTRAINT, 1452 => MDB2_ERROR_CONSTRAINT,
  204. 1542 => MDB2_ERROR_CANNOT_DROP, 1546 => MDB2_ERROR_CONSTRAINT, 1582 => MDB2_ERROR_CONSTRAINT,
  205. 2003 => MDB2_ERROR_CONNECT_FAILED, 2019 => MDB2_ERROR_INVALID);
  206. }
  207. if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS)
  208. {
  209. $ecode_map[1022] = MDB2_ERROR_CONSTRAINT;
  210. $ecode_map[1048] = MDB2_ERROR_CONSTRAINT_NOT_NULL;
  211. $ecode_map[1062] = MDB2_ERROR_CONSTRAINT;
  212. }
  213. else
  214. {
  215. // Doing this in case mode changes during runtime.
  216. $ecode_map[1022] = MDB2_ERROR_ALREADY_EXISTS;
  217. $ecode_map[1048] = MDB2_ERROR_CONSTRAINT;
  218. $ecode_map[1062] = MDB2_ERROR_ALREADY_EXISTS;
  219. }
  220. if (isset($ecode_map[$native_code]))
  221. {
  222. $error = $ecode_map[$native_code];
  223. }
  224. }
  225. return array($error, $native_code, $native_msg);
  226. }
  227. // }}}
  228. // {{{ escape()
  229. /**
  230. * Quotes a string so it can be safely used in a query. It will quote
  231. * the text so it can safely be used within a query.
  232. *
  233. * @param string the input string to quote
  234. * @param bool escape wildcards
  235. *
  236. * @return string quoted string
  237. *
  238. * @access public
  239. */
  240. function escape($text, $escape_wildcards = false)
  241. {
  242. if ($escape_wildcards)
  243. {
  244. $text = $this->escapePattern($text);
  245. }
  246. $connection = $this->getConnection();
  247. if (PEAR :: isError($connection))
  248. {
  249. return $connection;
  250. }
  251. $text = @mysql_real_escape_string($text, $connection);
  252. return $text;
  253. }
  254. // }}}
  255. // {{{ beginTransaction()
  256. /**
  257. * Start a transaction or set a savepoint.
  258. *
  259. * @param string name of a savepoint to set
  260. * @return mixed MDB2_OK on success, a MDB2 error on failure
  261. *
  262. * @access public
  263. */
  264. function beginTransaction($savepoint = null)
  265. {
  266. $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  267. 'savepoint' => $savepoint));
  268. $this->_getServerCapabilities();
  269. if (! is_null($savepoint))
  270. {
  271. if (! $this->supports('savepoints'))
  272. {
  273. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'savepoints are not supported', __FUNCTION__);
  274. }
  275. if (! $this->in_transaction)
  276. {
  277. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'savepoint cannot be released when changes are auto committed', __FUNCTION__);
  278. }
  279. $query = 'SAVEPOINT ' . $savepoint;
  280. return $this->_doQuery($query, true);
  281. }
  282. elseif ($this->in_transaction)
  283. {
  284. return MDB2_OK; //nothing to do
  285. }
  286. if (! $this->destructor_registered && $this->opened_persistent)
  287. {
  288. $this->destructor_registered = true;
  289. register_shutdown_function('MDB2_closeOpenTransactions');
  290. }
  291. $query = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 0';
  292. $result = & $this->_doQuery($query, true);
  293. if (PEAR :: isError($result))
  294. {
  295. return $result;
  296. }
  297. $this->in_transaction = true;
  298. return MDB2_OK;
  299. }
  300. // }}}
  301. // {{{ commit()
  302. /**
  303. * Commit the database changes done during a transaction that is in
  304. * progress or release a savepoint. This function may only be called when
  305. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  306. * transaction is implicitly started after committing the pending changes.
  307. *
  308. * @param string name of a savepoint to release
  309. * @return mixed MDB2_OK on success, a MDB2 error on failure
  310. *
  311. * @access public
  312. */
  313. function commit($savepoint = null)
  314. {
  315. $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  316. 'savepoint' => $savepoint));
  317. if (! $this->in_transaction)
  318. {
  319. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
  320. }
  321. if (! is_null($savepoint))
  322. {
  323. if (! $this->supports('savepoints'))
  324. {
  325. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'savepoints are not supported', __FUNCTION__);
  326. }
  327. $server_info = $this->getServerVersion();
  328. if (version_compare($server_info['major'] . '.' . $server_info['minor'] . '.' . $server_info['patch'], '5.0.3', '<'))
  329. {
  330. return MDB2_OK;
  331. }
  332. $query = 'RELEASE SAVEPOINT ' . $savepoint;
  333. return $this->_doQuery($query, true);
  334. }
  335. if (! $this->supports('transactions'))
  336. {
  337. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'transactions are not supported', __FUNCTION__);
  338. }
  339. $result = & $this->_doQuery('COMMIT', true);
  340. if (PEAR :: isError($result))
  341. {
  342. return $result;
  343. }
  344. if (! $this->start_transaction)
  345. {
  346. $query = 'SET AUTOCOMMIT = 1';
  347. $result = & $this->_doQuery($query, true);
  348. if (PEAR :: isError($result))
  349. {
  350. return $result;
  351. }
  352. }
  353. $this->in_transaction = false;
  354. return MDB2_OK;
  355. }
  356. // }}}
  357. // {{{ rollback()
  358. /**
  359. * Cancel any database changes done during a transaction or since a specific
  360. * savepoint that is in progress. This function may only be called when
  361. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  362. * transaction is implicitly started after canceling the pending changes.
  363. *
  364. * @param string name of a savepoint to rollback to
  365. * @return mixed MDB2_OK on success, a MDB2 error on failure
  366. *
  367. * @access public
  368. */
  369. function rollback($savepoint = null)
  370. {
  371. $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  372. 'savepoint' => $savepoint));
  373. if (! $this->in_transaction)
  374. {
  375. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'rollback cannot be done changes are auto committed', __FUNCTION__);
  376. }
  377. if (! is_null($savepoint))
  378. {
  379. if (! $this->supports('savepoints'))
  380. {
  381. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'savepoints are not supported', __FUNCTION__);
  382. }
  383. $query = 'ROLLBACK TO SAVEPOINT ' . $savepoint;
  384. return $this->_doQuery($query, true);
  385. }
  386. $query = 'ROLLBACK';
  387. $result = & $this->_doQuery($query, true);
  388. if (PEAR :: isError($result))
  389. {
  390. return $result;
  391. }
  392. if (! $this->start_transaction)
  393. {
  394. $query = 'SET AUTOCOMMIT = 1';
  395. $result = & $this->_doQuery($query, true);
  396. if (PEAR :: isError($result))
  397. {
  398. return $result;
  399. }
  400. }
  401. $this->in_transaction = false;
  402. return MDB2_OK;
  403. }
  404. // }}}
  405. // {{{ function setTransactionIsolation()
  406. /**
  407. * Set the transacton isolation level.
  408. *
  409. * @param string standard isolation level
  410. * READ UNCOMMITTED (allows dirty reads)
  411. * READ COMMITTED (prevents dirty reads)
  412. * REPEATABLE READ (prevents nonrepeatable reads)
  413. * SERIALIZABLE (prevents phantom reads)
  414. * @return mixed MDB2_OK on success, a MDB2 error on failure
  415. *
  416. * @access public
  417. * @since 2.1.1
  418. */
  419. function setTransactionIsolation($isolation)
  420. {
  421. $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
  422. if (! $this->supports('transactions'))
  423. {
  424. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'transactions are not supported', __FUNCTION__);
  425. }
  426. switch ($isolation)
  427. {
  428. case 'READ UNCOMMITTED' :
  429. case 'READ COMMITTED' :
  430. case 'REPEATABLE READ' :
  431. case 'SERIALIZABLE' :
  432. break;
  433. default :
  434. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'isolation level is not supported: ' . $isolation, __FUNCTION__);
  435. }
  436. $query = "SET SESSION TRANSACTION ISOLATION LEVEL $isolation";
  437. return $this->_doQuery($query, true);
  438. }
  439. // }}}
  440. // {{{ _doConnect()
  441. /**
  442. * do the grunt work of the connect
  443. *
  444. * @return connection on success or MDB2 Error Object on failure
  445. * @access protected
  446. */
  447. function _doConnect($username, $password, $persistent = false)
  448. {
  449. if (! PEAR :: loadExtension($this->phptype))
  450. {
  451. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'extension ' . $this->phptype . ' is not compiled into PHP', __FUNCTION__);
  452. }
  453. $params = array();
  454. if ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix')
  455. {
  456. $params[0] = ':' . $this->dsn['socket'];
  457. }
  458. else
  459. {
  460. $params[0] = $this->dsn['hostspec'] ? $this->dsn['hostspec'] : 'localhost';
  461. if ($this->dsn['port'])
  462. {
  463. $params[0] .= ':' . $this->dsn['port'];
  464. }
  465. }
  466. $params[] = $username ? $username : null;
  467. $params[] = $password ? $password : null;
  468. if (! $persistent)
  469. {
  470. if ($this->_isNewLinkSet())
  471. {
  472. $params[] = true;
  473. }
  474. else
  475. {
  476. $params[] = false;
  477. }
  478. }
  479. if (version_compare(phpversion(), '4.3.0', '>='))
  480. {
  481. $params[] = isset($this->dsn['client_flags']) ? $this->dsn['client_flags'] : null;
  482. }
  483. $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
  484. $connection = @call_user_func_array($connect_function, $params);
  485. if (! $connection)
  486. {
  487. if (($err = @mysql_error()) != '')
  488. {
  489. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, $err, __FUNCTION__);
  490. }
  491. else
  492. {
  493. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, 'unable to establish a connection', __FUNCTION__);
  494. }
  495. }
  496. if (! empty($this->dsn['charset']))
  497. {
  498. $result = $this->setCharset($this->dsn['charset'], $connection);
  499. if (PEAR :: isError($result))
  500. {
  501. $this->disconnect(false);
  502. return $result;
  503. }
  504. }
  505. return $connection;
  506. }
  507. // }}}
  508. // {{{ connect()
  509. /**
  510. * Connect to the database
  511. *
  512. * @return MDB2_OK on success, MDB2 Error Object on failure
  513. * @access public
  514. */
  515. function connect()
  516. {
  517. if (is_resource($this->connection))
  518. {
  519. //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
  520. if (MDB2 :: areEquals($this->connected_dsn, $this->dsn) && $this->opened_persistent == $this->options['persistent'])
  521. {
  522. return MDB2_OK;
  523. }
  524. $this->disconnect(false);
  525. }
  526. $connection = $this->_doConnect($this->dsn['username'], $this->dsn['password'], $this->options['persistent']);
  527. if (PEAR :: isError($connection))
  528. {
  529. return $connection;
  530. }
  531. $this->connection = $connection;
  532. $this->connected_dsn = $this->dsn;
  533. $this->connected_database_name = '';
  534. $this->opened_persistent = $this->options['persistent'];
  535. $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
  536. if ($this->database_name)
  537. {
  538. if ($this->database_name != $this->connected_database_name)
  539. {
  540. if (! @mysql_select_db($this->database_name, $connection))
  541. {
  542. $err = $this->raiseError(null, null, null, 'Could not select the database: ' . $this->database_name, __FUNCTION__);
  543. return $err;
  544. }
  545. $this->connected_database_name = $this->database_name;
  546. }
  547. }
  548. $this->_getServerCapabilities();
  549. return MDB2_OK;
  550. }
  551. // }}}
  552. // {{{ setCharset()
  553. /**
  554. * Set the charset on the current connection
  555. *
  556. * @param string charset (or array(charset, collation))
  557. * @param resource connection handle
  558. *
  559. * @return true on success, MDB2 Error Object on failure
  560. */
  561. function setCharset($charset, $connection = null)
  562. {
  563. if (is_null($connection))
  564. {
  565. $connection = $this->getConnection();
  566. if (PEAR :: isError($connection))
  567. {
  568. return $connection;
  569. }
  570. }
  571. $collation = null;
  572. if (is_array($charset) && 2 == count($charset))
  573. {
  574. $collation = array_pop($charset);
  575. $charset = array_pop($charset);
  576. }
  577. $client_info = mysql_get_client_info();
  578. if (function_exists('mysql_set_charset') && version_compare($client_info, '5.0.6'))
  579. {
  580. if (! $result = mysql_set_charset($charset, $connection))
  581. {
  582. $err = & $this->raiseError(null, null, null, 'Could not set client character set', __FUNCTION__);
  583. return $err;
  584. }
  585. return $result;
  586. }
  587. $query = "SET NAMES '" . mysql_real_escape_string($charset, $connection) . "'";
  588. if (! is_null($collation))
  589. {
  590. $query .= " COLLATE '" . mysqli_real_escape_string($connection, $collation) . "'";
  591. }
  592. return $this->_doQuery($query, true, $connection);
  593. }
  594. // }}}
  595. // {{{ databaseExists()
  596. /**
  597. * check if given database name is exists?
  598. *
  599. * @param string $name name of the database that should be checked
  600. *
  601. * @return mixed true/false on success, a MDB2 error on failure
  602. * @access public
  603. */
  604. function databaseExists($name)
  605. {
  606. $connection = $this->_doConnect($this->dsn['username'], $this->dsn['password'], $this->options['persistent']);
  607. if (PEAR :: isError($connection))
  608. {
  609. return $connection;
  610. }
  611. $result = @mysql_select_db($name, $connection);
  612. @mysql_close($connection);
  613. return $result;
  614. }
  615. // }}}
  616. // {{{ disconnect()
  617. /**
  618. * Log out and disconnect from the database.
  619. *
  620. * @param boolean $force if the disconnect should be forced even if the
  621. * connection is opened persistently
  622. * @return mixed true on success, false if not connected and error
  623. * object on error
  624. * @access public
  625. */
  626. function disconnect($force = true)
  627. {
  628. if (is_resource($this->connection))
  629. {
  630. if ($this->in_transaction)
  631. {
  632. $dsn = $this->dsn;
  633. $database_name = $this->database_name;
  634. $persistent = $this->options['persistent'];
  635. $this->dsn = $this->connected_dsn;
  636. $this->database_name = $this->connected_database_name;
  637. $this->options['persistent'] = $this->opened_persistent;
  638. $this->rollback();
  639. $this->dsn = $dsn;
  640. $this->database_name = $database_name;
  641. $this->options['persistent'] = $persistent;
  642. }
  643. if (! $this->opened_persistent || $force)
  644. {
  645. $ok = @mysql_close($this->connection);
  646. if (! $ok)
  647. {
  648. return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED, null, null, null, __FUNCTION__);
  649. }
  650. }
  651. }
  652. else
  653. {
  654. return false;
  655. }
  656. return parent :: disconnect($force);
  657. }
  658. // }}}
  659. // {{{ standaloneQuery()
  660. /**
  661. * execute a query as DBA
  662. *
  663. * @param string $query the SQL query
  664. * @param mixed $types array that contains the types of the columns in
  665. * the result set
  666. * @param boolean $is_manip if the query is a manipulation query
  667. * @return mixed MDB2_OK on success, a MDB2 error on failure
  668. * @access public
  669. */
  670. function &standaloneQuery($query, $types = null, $is_manip = false)
  671. {
  672. $user = $this->options['DBA_username'] ? $this->options['DBA_username'] : $this->dsn['username'];
  673. $pass = $this->options['DBA_password'] ? $this->options['DBA_password'] : $this->dsn['password'];
  674. $connection = $this->_doConnect($user, $pass, $this->options['persistent']);
  675. if (PEAR :: isError($connection))
  676. {
  677. return $connection;
  678. }
  679. $offset = $this->offset;
  680. $limit = $this->limit;
  681. $this->offset = $this->limit = 0;
  682. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  683. $result = & $this->_doQuery($query, $is_manip, $connection, $this->database_name);
  684. if (! PEAR :: isError($result))
  685. {
  686. $result = $this->_affectedRows($connection, $result);
  687. }
  688. @mysql_close($connection);
  689. return $result;
  690. }
  691. // }}}
  692. // {{{ _doQuery()
  693. /**
  694. * Execute a query
  695. * @param string $query query
  696. * @param boolean $is_manip if the query is a manipulation query
  697. * @param resource $connection
  698. * @param string $database_name
  699. * @return result or error object
  700. * @access protected
  701. */
  702. function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  703. {
  704. $this->last_query = $query;
  705. $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  706. if ($result)
  707. {
  708. if (PEAR :: isError($result))
  709. {
  710. return $result;
  711. }
  712. $query = $result;
  713. }
  714. if ($this->options['disable_query'])
  715. {
  716. $result = $is_manip ? 0 : null;
  717. return $result;
  718. }
  719. if (is_null($connection))
  720. {
  721. $connection = $this->getConnection();
  722. if (PEAR :: isError($connection))
  723. {
  724. return $connection;
  725. }
  726. }
  727. if (is_null($database_name))
  728. {
  729. $database_name = $this->database_name;
  730. }
  731. if ($database_name)
  732. {
  733. if ($database_name != $this->connected_database_name)
  734. {
  735. if (! @mysql_select_db($database_name, $connection))
  736. {
  737. $err = $this->raiseError(null, null, null, 'Could not select the database: ' . $database_name, __FUNCTION__);
  738. return $err;
  739. }
  740. $this->connected_database_name = $database_name;
  741. }
  742. }
  743. $function = $this->options['result_buffering'] ? 'mysql_query' : 'mysql_unbuffered_query';
  744. $result = @$function($query, $connection);
  745. if (! $result)
  746. {
  747. $err = & $this->raiseError(null, null, null, 'Could not execute statement', __FUNCTION__);
  748. return $err;
  749. }
  750. $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
  751. return $result;
  752. }
  753. // }}}
  754. // {{{ _affectedRows()
  755. /**
  756. * Returns the number of rows affected
  757. *
  758. * @param resource $result
  759. * @param resource $connection
  760. * @return mixed MDB2 Error Object or the number of rows affected
  761. * @access private
  762. */
  763. function _affectedRows($connection, $result = null)
  764. {
  765. if (is_null($connection))
  766. {
  767. $connection = $this->getConnection();
  768. if (PEAR :: isError($connection))
  769. {
  770. return $connection;
  771. }
  772. }
  773. return @mysql_affected_rows($connection);
  774. }
  775. // }}}
  776. // {{{ _modifyQuery()
  777. /**
  778. * Changes a query string for various DBMS specific reasons
  779. *
  780. * @param string $query query to modify
  781. * @param boolean $is_manip if it is a DML query
  782. * @param integer $limit limit the number of rows
  783. * @param integer $offset start reading from given offset
  784. * @return string modified query
  785. * @access protected
  786. */
  787. function _modifyQuery($query, $is_manip, $limit, $offset)
  788. {
  789. if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT)
  790. {
  791. // "DELETE FROM table" gives 0 affected rows in MySQL.
  792. // This little hack lets you know how many rows were deleted.
  793. if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query))
  794. {
  795. $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', 'DELETE FROM \1 WHERE 1=1', $query);
  796. }
  797. }
  798. if ($limit > 0 && ! preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query))
  799. {
  800. $query = rtrim($query);
  801. if (substr($query, - 1) == ';')
  802. {
  803. $query = substr($query, 0, - 1);
  804. }
  805. // LIMIT doesn't always come last in the query
  806. // @see http://dev.mysql.com/doc/refman/5.0/en/select.html
  807. $after = '';
  808. if (preg_match('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', $query, $matches))
  809. {
  810. $after = $matches[0];
  811. $query = preg_replace('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', '', $query);
  812. }
  813. elseif (preg_match('/(\s+FOR\s+UPDATE\s*)$/i', $query, $matches))
  814. {
  815. $after = $matches[0];
  816. $query = preg_replace('/(\s+FOR\s+UPDATE\s*)$/im', '', $query);
  817. }
  818. elseif (preg_match('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', $query, $matches))
  819. {
  820. $after = $matches[0];
  821. $query = preg_replace('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', '', $query);
  822. }
  823. if ($is_manip)
  824. {
  825. return $query . " LIMIT $limit" . $after;
  826. }
  827. else
  828. {
  829. return $query . " LIMIT $offset, $limit" . $after;
  830. }
  831. }
  832. return $query;
  833. }
  834. // }}}
  835. // {{{ getServerVersion()
  836. /**
  837. * return version information about the server
  838. *
  839. * @param bool $native determines if the raw version string should be returned
  840. * @return mixed array/string with version information or MDB2 error object
  841. * @access public
  842. */
  843. function getServerVersion($native = false)
  844. {
  845. $connection = $this->getConnection();
  846. if (PEAR :: isError($connection))
  847. {
  848. return $connection;
  849. }
  850. if ($this->connected_server_info)
  851. {
  852. $server_info = $this->connected_server_info;
  853. }
  854. else
  855. {
  856. $server_info = @mysql_get_server_info($connection);
  857. }
  858. if (! $server_info)
  859. {
  860. return $this->raiseError(null, null, null, 'Could not get server information', __FUNCTION__);
  861. }
  862. // cache server_info
  863. $this->connected_server_info = $server_info;
  864. if (! $native)
  865. {
  866. $tmp = explode('.', $server_info, 3);
  867. if (isset($tmp[2]) && strpos($tmp[2], '-'))
  868. {
  869. $tmp2 = explode('-', @$tmp[2], 2);
  870. }
  871. else
  872. {
  873. $tmp2[0] = isset($tmp[2]) ? $tmp[2] : null;
  874. $tmp2[1] = null;
  875. }
  876. $server_info = array('major' => isset($tmp[0]) ? $tmp[0] : null, 'minor' => isset($tmp[1]) ? $tmp[1] : null,
  877. 'patch' => $tmp2[0], 'extra' => $tmp2[1], 'native' => $server_info);
  878. }
  879. return $server_info;
  880. }
  881. // }}}
  882. // {{{ _getServerCapabilities()
  883. /**
  884. * Fetch some information about the server capabilities
  885. * (transactions, subselects, prepared statements, etc).
  886. *
  887. * @access private
  888. */
  889. function _getServerCapabilities()
  890. {
  891. if (! $this->server_capabilities_checked)
  892. {
  893. $this->server_capabilities_checked = true;
  894. //set defaults
  895. $this->supported['sub_selects'] = 'emulated';
  896. $this->supported['prepared_statements'] = 'emulated';
  897. $this->supported['triggers'] = false;
  898. $this->start_transaction = false;
  899. $this->varchar_max_length = 255;
  900. $server_info = $this->getServerVersion();
  901. if (is_array($server_info))
  902. {
  903. $server_version = $server_info['major'] . '.' . $server_info['minor'] . '.' . $server_info['patch'];
  904. if (! version_compare($server_version, '4.1.0', '<'))
  905. {
  906. $this->supported['sub_selects'] = true;
  907. $this->supported['prepared_statements'] = true;
  908. }
  909. // SAVEPOINTs were introduced in MySQL 4.0.14 and 4.1.1 (InnoDB)
  910. if (version_compare($server_version, '4.1.0', '>='))
  911. {
  912. if (version_compare($server_version, '4.1.1', '<'))
  913. {
  914. $this->supported['savepoints'] = false;
  915. }
  916. }
  917. elseif (version_compare($server_version, '4.0.14', '<'))
  918. {
  919. $this->supported['savepoints'] = false;
  920. }
  921. if (! version_compare($server_version, '4.0.11', '<'))
  922. {
  923. $this->start_transaction = true;
  924. }
  925. if (! version_compare($server_version, '5.0.3', '<'))
  926. {
  927. $this->varchar_max_length = 65532;
  928. }
  929. if (! version_compare($server_version, '5.0.2', '<'))
  930. {
  931. $this->supported['triggers'] = true;
  932. }
  933. }
  934. }
  935. }
  936. // }}}
  937. // {{{ function _skipUserDefinedVariable($query, $position)
  938. /**
  939. * Utility method, used by prepare() to avoid misinterpreting MySQL user
  940. * defined variables (SELECT @x:=5) for placeholders.
  941. * Check if the placeholder is a false positive, i.e. if it is an user defined
  942. * variable instead. If so, skip it and advance the position, otherwise
  943. * return the current position, which is valid
  944. *
  945. * @param string $query
  946. * @param integer $position current string cursor position
  947. * @return integer $new_position
  948. * @access protected
  949. */
  950. function _skipUserDefinedVariable($query, $position)
  951. {
  952. $found = strpos(strrev(substr($query, 0, $position)), '@');
  953. if ($found === false)
  954. {
  955. return $position;
  956. }
  957. $pos = strlen($query) - strlen(substr($query, $position)) - $found - 1;
  958. $substring = substr($query, $pos, $position - $pos + 2);
  959. if (preg_match('/^@\w+\s*:=$/', $substring))
  960. {
  961. return $position + 1; //found an user defined variable: skip it
  962. }
  963. return $position;
  964. }
  965. // }}}
  966. // {{{ prepare()
  967. /**
  968. * Prepares a query for multiple execution with execute().
  969. * With some database backends, this is emulated.
  970. * prepare() requires a generic query as string like
  971. * 'INSERT INTO numbers VALUES(?,?)' or
  972. * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  973. * The ? and :name and are placeholders which can be set using
  974. * bindParam() and the query can be sent off using the execute() method.
  975. * The allowed format for :name can be set with the 'bindname_format' option.
  976. *
  977. * @param string $query the query to prepare
  978. * @param mixed $types array that contains the types of the placeholders
  979. * @param mixed $result_types array that contains the types of the columns in
  980. * the result set or MDB2_PREPARE_RESULT, if set to
  981. * MDB2_PREPARE_MANIP the query is handled as a manipulation query
  982. * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
  983. * @return mixed resource handle for the prepared query on success, a MDB2
  984. * error on failure
  985. * @access public
  986. * @see bindParam, execute
  987. */
  988. function &prepare($query, $types = null, $result_types = null, $lobs = array())
  989. {
  990. if ($this->options['emulate_prepared'] || $this->supported['prepared_statements'] !== true)
  991. {
  992. $obj = & parent :: prepare($query, $types, $result_types, $lobs);
  993. return $obj;
  994. }
  995. $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  996. $offset = $this->offset;
  997. $limit = $this->limit;
  998. $this->offset = $this->limit = 0;
  999. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  1000. $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
  1001. if ($result)
  1002. {
  1003. if (PEAR :: isError($result))
  1004. {
  1005. return $result;
  1006. }
  1007. $query = $result;
  1008. }
  1009. $placeholder_type_guess = $placeholder_type = null;
  1010. $question = '?';
  1011. $colon = ':';
  1012. $positions = array();
  1013. $position = 0;
  1014. while ($position < strlen($query))
  1015. {
  1016. $q_position = strpos($query, $question, $position);
  1017. $c_position = strpos($query, $colon, $position);
  1018. if ($q_position && $c_position)
  1019. {
  1020. $p_position = min($q_position, $c_position);
  1021. }
  1022. elseif ($q_position)
  1023. {
  1024. $p_position = $q_position;
  1025. }
  1026. elseif ($c_position)
  1027. {
  1028. $p_position = $c_position;
  1029. }
  1030. else
  1031. {
  1032. break;
  1033. }
  1034. if (is_null($placeholder_type))
  1035. {
  1036. $placeholder_type_guess = $query[$p_position];
  1037. }
  1038. $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
  1039. if (PEAR :: isError($new_pos))
  1040. {
  1041. return $new_pos;
  1042. }
  1043. if ($new_pos != $position)
  1044. {
  1045. $position = $new_pos;
  1046. continue; //evaluate again starting from the new position
  1047. }
  1048. //make sure this is not part of an user defined variable
  1049. $new_pos = $this->_skipUserDefinedVariable($query, $position);
  1050. if ($new_pos != $position)
  1051. {
  1052. $position = $new_pos;
  1053. continue; //evaluate again starting from the new position
  1054. }
  1055. if ($query[$position] == $placeholder_type_guess)
  1056. {
  1057. if (is_null($placeholder_type))
  1058. {
  1059. $placeholder_type = $query[$p_position];
  1060. $question = $colon = $placeholder_type;
  1061. }
  1062. if ($placeholder_type == ':')
  1063. {
  1064. $regexp = '/^.{' . ($position + 1) . '}(' . $this->options['bindname_format'] . ').*$/s';
  1065. $parameter = preg_replace($regexp, '\\1', $query);
  1066. if ($parameter === '')
  1067. {
  1068. $err = & $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'named parameter name must match "bindname_format" option', __FUNCTION__);
  1069. return $err;
  1070. }
  1071. $positions[$p_position] = $parameter;
  1072. $query = substr_replace($query, '?', $position, strlen($parameter) + 1);
  1073. }
  1074. else
  1075. {
  1076. $positions[$p_position] = count($positions);
  1077. }
  1078. $position = $p_position + 1;
  1079. }
  1080. else
  1081. {
  1082. $position = $p_position;
  1083. }
  1084. }
  1085. $connection = $this->getConnection();
  1086. if (PEAR :: isError($connection))
  1087. {
  1088. return $connection;
  1089. }
  1090. static $prep_statement_counter = 1;
  1091. $statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter ++ . sha1(microtime() + mt_rand()));
  1092. $statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']);
  1093. $query = "PREPARE $statement_name FROM " . $this->quote($query, 'text');
  1094. $statement = & $this->_doQuery($query, true, $connection);
  1095. if (PEAR :: isError($statement))
  1096. {
  1097. return $statement;
  1098. }
  1099. $class_name = 'MDB2_Statement_' . $this->phptype;
  1100. $obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  1101. $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
  1102. return $obj;
  1103. }
  1104. // }}}
  1105. // {{{ replace()
  1106. /**
  1107. * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  1108. * query, except that if there is already a row in the table with the same
  1109. * key field values, the old row is deleted before the new row is inserted.
  1110. *
  1111. * The REPLACE type of query does not make part of the SQL standards. Since
  1112. * practically only MySQL implements it natively, this type of query is
  1113. * emulated through this method for other DBMS using standard types of
  1114. * queries inside a transaction to assure the atomicity of the operation.
  1115. *
  1116. * @access public
  1117. *
  1118. * @param string $table name of the table on which the REPLACE query will
  1119. * be executed.
  1120. * @param array $fields associative array that describes the fields and the
  1121. * values that will be inserted or updated in the specified table. The
  1122. * indexes of the array are the names of all the fields of the table. The
  1123. * values of the array are also associative arrays that describe the
  1124. * values and other properties of the table fields.
  1125. *
  1126. * Here follows a list of field properties that need to be specified:
  1127. *
  1128. * value:
  1129. * Value to be assigned to the specified field. This value may be
  1130. * of specified in database independent type format as this
  1131. * function can perform the necessary datatype conversions.
  1132. *
  1133. * Default:
  1134. * this property is required unless the Null property
  1135. * is set to 1.
  1136. *
  1137. * type
  1138. * Name of the type of the field. Currently, all types Metabase
  1139. * are supported except for clob and blob.
  1140. *
  1141. * Default: no type conversion
  1142. *
  1143. * null
  1144. * Boolean property that indicates that the value for this field
  1145. * should be set to null.
  1146. *
  1147. * The default value for fields missing in INSERT queries may be
  1148. * specified the definition of a table. Often, the default value
  1149. * is already null, but since the REPLACE may be emulated using
  1150. * an UPDATE query, make sure that all fields of the table are
  1151. * listed in this function argument array.
  1152. *
  1153. * Default: 0
  1154. *
  1155. * key
  1156. * Boolean property that indicates that this field should be
  1157. * handled as a primary key or at least as part of the compound
  1158. * unique index of the table that will determine the row that will
  1159. * updated if it exists or inserted a new row otherwise.
  1160. *
  1161. * This function will fail if no key field is specified or if the
  1162. * value of a key field is set to null because fields that are
  1163. * part of unique index they may not be null.
  1164. *
  1165. * Default: 0
  1166. *
  1167. * @see http://dev.mysql.com/doc/refman/5.0/en/replace.html
  1168. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1169. */
  1170. function replace($table, $fields)
  1171. {
  1172. $count = count($fields);
  1173. $query = $values = '';
  1174. $keys = $colnum = 0;
  1175. for(reset($fields); $colnum < $count; next($fields), $colnum ++)
  1176. {
  1177. $name = key($fields);
  1178. if ($colnum > 0)
  1179. {
  1180. $query .= ',';
  1181. $values .= ',';
  1182. }
  1183. $query .= $this->quoteIdentifier($name, true);
  1184. if (isset($fields[$name]['null']) && $fields[$name]['null'])
  1185. {
  1186. $value = 'NULL';
  1187. }
  1188. else
  1189. {
  1190. $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
  1191. $value = $this->quote($fields[$name]['value'], $type);
  1192. if (PEAR :: isError($value))
  1193. {
  1194. return $value;
  1195. }
  1196. }
  1197. $values .= $value;
  1198. if (isset($fields[$name]['key']) && $fields[$name]['key'])
  1199. {
  1200. if ($value === 'NULL')
  1201. {
  1202. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'key value ' . $name . ' may not be NULL', __FUNCTION__);
  1203. }
  1204. $keys ++;
  1205. }
  1206. }
  1207. if ($keys == 0)
  1208. {
  1209. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'not specified which fields are keys', __FUNCTION__);
  1210. }
  1211. $connection = $this->getConnection();
  1212. if (PEAR :: isError($connection))
  1213. {
  1214. return $connection;
  1215. }
  1216. $table = $this->quoteIdentif

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