PageRenderTime 122ms CodeModel.GetById 40ms RepoModel.GetById 4ms app.codeStats 0ms

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

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 1699 lines | 1073 code | 164 blank | 462 comment | 172 complexity | 19bb3a0f7f107a677cdac3dd4e014893 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, Lorenzo Alberton |
  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: Lorenzo Alberton <l.alberton@quipo.it> |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: ibase.php 137 2009-11-09 13:24:37Z vanpouckesven $
  47. /**
  48. * MDB2 FireBird/InterBase driver
  49. *
  50. * @package MDB2
  51. * @category Database
  52. * @author Lorenzo Alberton <l.alberton@quipo.it>
  53. */
  54. class MDB2_Driver_ibase extends MDB2_Driver_Common
  55. {
  56. // {{{ properties
  57. var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => '\\');
  58. var $identifier_quoting = array('start' => '', 'end' => '', 'escape' => false);
  59. var $transaction_id = 0;
  60. var $query_parameters = array();
  61. var $query_parameter_values = array();
  62. // }}}
  63. // {{{ constructor
  64. /**
  65. * Constructor
  66. */
  67. function __construct()
  68. {
  69. parent :: __construct();
  70. $this->phptype = 'ibase';
  71. $this->dbsyntax = 'ibase';
  72. $this->supported['sequences'] = true;
  73. $this->supported['indexes'] = true;
  74. $this->supported['affected_rows'] = function_exists('ibase_affected_rows');
  75. $this->supported['summary_functions'] = true;
  76. $this->supported['order_by_text'] = true;
  77. $this->supported['transactions'] = true;
  78. $this->supported['savepoints'] = true;
  79. $this->supported['current_id'] = true;
  80. $this->supported['limit_queries'] = 'emulated';
  81. $this->supported['LOBs'] = true;
  82. $this->supported['replace'] = false;
  83. $this->supported['sub_selects'] = true;
  84. $this->supported['triggers'] = true;
  85. $this->supported['auto_increment'] = true;
  86. $this->supported['primary_key'] = true;
  87. $this->supported['result_introspection'] = true;
  88. $this->supported['prepared_statements'] = true;
  89. $this->supported['identifier_quoting'] = false;
  90. $this->supported['pattern_escaping'] = true;
  91. $this->supported['new_link'] = false;
  92. $this->options['DBA_username'] = false;
  93. $this->options['DBA_password'] = false;
  94. $this->options['database_path'] = '';
  95. $this->options['database_extension'] = '.gdb';
  96. $this->options['server_version'] = '';
  97. $this->options['max_identifiers_length'] = 31;
  98. }
  99. // }}}
  100. // {{{ errorInfo()
  101. /**
  102. * This method is used to collect information about an error
  103. *
  104. * @param integer $error
  105. * @return array
  106. * @access public
  107. */
  108. function errorInfo($error = null)
  109. {
  110. $native_msg = @ibase_errmsg();
  111. if (function_exists('ibase_errcode'))
  112. {
  113. $native_code = @ibase_errcode();
  114. }
  115. else
  116. {
  117. // memo for the interbase php module hackers: we need something similar
  118. // to mysql_errno() to retrieve error codes instead of this ugly hack
  119. if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_msg, $m))
  120. {
  121. $native_code = (int) $m[2];
  122. }
  123. else
  124. {
  125. $native_code = null;
  126. }
  127. }
  128. if (is_null($error))
  129. {
  130. $error = MDB2_ERROR;
  131. if ($native_code)
  132. {
  133. // try to interpret Interbase error code (that's why we need ibase_errno()
  134. // in the interbase module to return the real error code)
  135. switch ($native_code)
  136. {
  137. case - 204 :
  138. if (isset($m[3]) && is_int(strpos($m[3], 'Table unknown')))
  139. {
  140. $errno = MDB2_ERROR_NOSUCHTABLE;
  141. }
  142. break;
  143. default :
  144. static $ecode_map;
  145. if (empty($ecode_map))
  146. {
  147. $ecode_map = array(- 104 => MDB2_ERROR_SYNTAX, - 150 => MDB2_ERROR_ACCESS_VIOLATION,
  148. - 151 => MDB2_ERROR_ACCESS_VIOLATION, - 155 => MDB2_ERROR_NOSUCHTABLE,
  149. - 157 => MDB2_ERROR_NOSUCHFIELD, - 158 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  150. - 170 => MDB2_ERROR_MISMATCH, - 171 => MDB2_ERROR_MISMATCH,
  151. - 172 => MDB2_ERROR_INVALID, // -204 => // Covers too many errors, need to use regex on msg
  152. - 205 => MDB2_ERROR_NOSUCHFIELD,
  153. - 206 => MDB2_ERROR_NOSUCHFIELD, - 208 => MDB2_ERROR_INVALID,
  154. - 219 => MDB2_ERROR_NOSUCHTABLE, - 297 => MDB2_ERROR_CONSTRAINT,
  155. - 303 => MDB2_ERROR_INVALID, - 413 => MDB2_ERROR_INVALID_NUMBER,
  156. - 530 => MDB2_ERROR_CONSTRAINT, - 551 => MDB2_ERROR_ACCESS_VIOLATION,
  157. - 552 => MDB2_ERROR_ACCESS_VIOLATION, // -607 => // Covers too many errors, need to use regex on msg
  158. - 625 => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  159. - 803 => MDB2_ERROR_CONSTRAINT, - 804 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  160. // -902 => // Covers too many errors, need to use regex on msg
  161. - 904 => MDB2_ERROR_CONNECT_FAILED,
  162. - 922 => MDB2_ERROR_NOSUCHDB, - 923 => MDB2_ERROR_CONNECT_FAILED,
  163. - 924 => MDB2_ERROR_CONNECT_FAILED);
  164. }
  165. if (isset($ecode_map[$native_code]))
  166. {
  167. $error = $ecode_map[$native_code];
  168. }
  169. break;
  170. }
  171. }
  172. else
  173. {
  174. static $error_regexps;
  175. if (! isset($error_regexps))
  176. {
  177. $error_regexps = array(
  178. '/generator .* is not defined/' => MDB2_ERROR_SYNTAX, // for compat. w ibase_errcode()
  179. '/table.*(not exist|not found|unknown)/i' => MDB2_ERROR_NOSUCHTABLE,
  180. '/table .* already exists/i' => MDB2_ERROR_ALREADY_EXISTS,
  181. '/unsuccessful metadata update .* failed attempt to store duplicate value/i' => MDB2_ERROR_ALREADY_EXISTS,
  182. '/unsuccessful metadata update .* not found/i' => MDB2_ERROR_NOT_FOUND,
  183. '/validation error for column .* value "\*\*\* null/i' => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  184. '/violation of [\w ]+ constraint/i' => MDB2_ERROR_CONSTRAINT,
  185. '/conversion error from string/i' => MDB2_ERROR_INVALID_NUMBER,
  186. '/no permission for/i' => MDB2_ERROR_ACCESS_VIOLATION,
  187. '/arithmetic exception, numeric overflow, or string truncation/i' => MDB2_ERROR_INVALID,
  188. '/feature is not supported/i' => MDB2_ERROR_NOT_CAPABLE);
  189. }
  190. foreach ($error_regexps as $regexp => $code)
  191. {
  192. if (preg_match($regexp, $native_msg, $m))
  193. {
  194. $error = $code;
  195. break;
  196. }
  197. }
  198. }
  199. }
  200. return array($error, $native_code, $native_msg);
  201. }
  202. // }}}
  203. // {{{ quoteIdentifier()
  204. /**
  205. * Delimited identifiers are a nightmare with InterBase, so they're disabled
  206. *
  207. * @param string $str identifier name to be quoted
  208. * @param bool $check_option check the 'quote_identifier' option
  209. *
  210. * @return string quoted identifier string
  211. *
  212. * @access public
  213. */
  214. function quoteIdentifier($str, $check_option = false)
  215. {
  216. if ($check_option && ! $this->options['quote_identifier'])
  217. {
  218. return $str;
  219. }
  220. return strtoupper($str);
  221. }
  222. // }}}
  223. // {{{ getConnection()
  224. /**
  225. * Returns a native connection
  226. *
  227. * @return mixed a valid MDB2 connection object,
  228. * or a MDB2 error object on error
  229. * @access public
  230. */
  231. function getConnection()
  232. {
  233. $result = $this->connect();
  234. if (PEAR :: isError($result))
  235. {
  236. return $result;
  237. }
  238. if ($this->in_transaction)
  239. {
  240. return $this->transaction_id;
  241. }
  242. return $this->connection;
  243. }
  244. // }}}
  245. // {{{ beginTransaction()
  246. /**
  247. * Start a transaction or set a savepoint.
  248. *
  249. * @param string name of a savepoint to set
  250. * @return mixed MDB2_OK on success, a MDB2 error on failure
  251. *
  252. * @access public
  253. */
  254. function beginTransaction($savepoint = null)
  255. {
  256. $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  257. 'savepoint' => $savepoint));
  258. if (! is_null($savepoint))
  259. {
  260. if (! $this->in_transaction)
  261. {
  262. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'savepoint cannot be released when changes are auto committed', __FUNCTION__);
  263. }
  264. $query = 'SAVEPOINT ' . $savepoint;
  265. return $this->_doQuery($query, true);
  266. }
  267. elseif ($this->in_transaction)
  268. {
  269. return MDB2_OK; //nothing to do
  270. }
  271. $connection = $this->getConnection();
  272. if (PEAR :: isError($connection))
  273. {
  274. return $connection;
  275. }
  276. $result = @ibase_trans(IBASE_DEFAULT, $connection);
  277. if (! $result)
  278. {
  279. return $this->raiseError(null, null, null, 'could not start a transaction', __FUNCTION__);
  280. }
  281. $this->transaction_id = $result;
  282. $this->in_transaction = true;
  283. return MDB2_OK;
  284. }
  285. // }}}
  286. // {{{ commit()
  287. /**
  288. * Commit the database changes done during a transaction that is in
  289. * progress or release a savepoint. This function may only be called when
  290. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  291. * transaction is implicitly started after committing the pending changes.
  292. *
  293. * @param string name of a savepoint to release
  294. * @return mixed MDB2_OK on success, a MDB2 error on failure
  295. *
  296. * @access public
  297. */
  298. function commit($savepoint = null)
  299. {
  300. $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  301. 'savepoint' => $savepoint));
  302. if (! $this->in_transaction)
  303. {
  304. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
  305. }
  306. if (! is_null($savepoint))
  307. {
  308. $query = 'RELEASE SAVEPOINT ' . $savepoint;
  309. return $this->_doQuery($query, true);
  310. }
  311. if (! @ibase_commit($this->transaction_id))
  312. {
  313. return $this->raiseError(null, null, null, 'could not commit a transaction', __FUNCTION__);
  314. }
  315. $this->in_transaction = false;
  316. $this->transaction_id = 0;
  317. return MDB2_OK;
  318. }
  319. // }}}
  320. // {{{ rollback()
  321. /**
  322. * Cancel any database changes done during a transaction or since a specific
  323. * savepoint that is in progress. This function may only be called when
  324. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  325. * transaction is implicitly started after canceling the pending changes.
  326. *
  327. * @param string name of a savepoint to rollback to
  328. * @return mixed MDB2_OK on success, a MDB2 error on failure
  329. *
  330. * @access public
  331. */
  332. function rollback($savepoint = null)
  333. {
  334. $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  335. 'savepoint' => $savepoint));
  336. if (! $this->in_transaction)
  337. {
  338. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'rollback cannot be done changes are auto committed', __FUNCTION__);
  339. }
  340. if (! is_null($savepoint))
  341. {
  342. $query = 'ROLLBACK TO SAVEPOINT ' . $savepoint;
  343. return $this->_doQuery($query, true);
  344. }
  345. if ($this->transaction_id && ! @ibase_rollback($this->transaction_id))
  346. {
  347. return $this->raiseError(null, null, null, 'Could not rollback a pending transaction: ' . @ibase_errmsg(), __FUNCTION__);
  348. }
  349. $this->in_transaction = false;
  350. $this->transaction_id = 0;
  351. return MDB2_OK;
  352. }
  353. // }}}
  354. // {{{ setTransactionIsolation()
  355. /**
  356. * Set the transacton isolation level.
  357. *
  358. * @param string standard isolation level (SQL-92)
  359. * READ UNCOMMITTED (allows dirty reads)
  360. * READ COMMITTED (prevents dirty reads)
  361. * REPEATABLE READ (prevents nonrepeatable reads)
  362. * SERIALIZABLE (prevents phantom reads)
  363. * @param array some transaction options:
  364. * 'wait' => 'WAIT' | 'NO WAIT'
  365. * 'rw' => 'READ WRITE' | 'READ ONLY'
  366. * @return mixed MDB2_OK on success, a MDB2 error on failure
  367. *
  368. * @access public
  369. * @since 2.1.1
  370. */
  371. function setTransactionIsolation($isolation, $options = array())
  372. {
  373. $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
  374. switch ($isolation)
  375. {
  376. case 'READ UNCOMMITTED' :
  377. $ibase_isolation = 'READ COMMITTED RECORD_VERSION';
  378. break;
  379. case 'READ COMMITTED' :
  380. $ibase_isolation = 'READ COMMITTED NO RECORD_VERSION';
  381. break;
  382. case 'REPEATABLE READ' :
  383. $ibase_isolation = 'SNAPSHOT';
  384. break;
  385. case 'SERIALIZABLE' :
  386. $ibase_isolation = 'SNAPSHOT TABLE STABILITY';
  387. break;
  388. default :
  389. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'isolation level is not supported: ' . $isolation, __FUNCTION__);
  390. }
  391. if (! empty($options['wait']))
  392. {
  393. switch ($options['wait'])
  394. {
  395. case 'WAIT' :
  396. case 'NO WAIT' :
  397. $wait = $options['wait'];
  398. break;
  399. default :
  400. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'wait option is not supported: ' . $options['wait'], __FUNCTION__);
  401. }
  402. }
  403. if (! empty($options['rw']))
  404. {
  405. switch ($options['rw'])
  406. {
  407. case 'READ ONLY' :
  408. case 'READ WRITE' :
  409. $rw = $options['wait'];
  410. break;
  411. default :
  412. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'wait option is not supported: ' . $options['rw'], __FUNCTION__);
  413. }
  414. }
  415. $query = "SET TRANSACTION $rw $wait ISOLATION LEVEL $ibase_isolation";
  416. return $this->_doQuery($query, true);
  417. }
  418. // }}}
  419. // {{{ getDatabaseFile($database_name)
  420. /**
  421. * Builds the string with path+dbname+extension
  422. *
  423. * @return string full database path+file
  424. * @access protected
  425. */
  426. function _getDatabaseFile($database_name)
  427. {
  428. if ($database_name == '')
  429. {
  430. return $database_name;
  431. }
  432. $ret = $this->options['database_path'] . $database_name;
  433. if (! preg_match('/\.[fg]db$/i', $database_name))
  434. {
  435. $ret .= $this->options['database_extension'];
  436. }
  437. return $ret;
  438. }
  439. // }}}
  440. // {{{ _doConnect()
  441. /**
  442. * Does the grunt work of connecting to the database
  443. *
  444. * @return mixed connection resource on success, MDB2 Error Object on failure
  445. * @access protected
  446. */
  447. function _doConnect($username, $password, $database_name, $persistent = false)
  448. {
  449. if (! PEAR :: loadExtension('interbase'))
  450. {
  451. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'extension ' . $this->phptype . ' is not compiled into PHP', __FUNCTION__);
  452. }
  453. $database_file = $this->_getDatabaseFile($database_name);
  454. $dbhost = $this->dsn['hostspec'] ? ($this->dsn['hostspec'] . ':' . $database_file) : $database_file;
  455. $params = array();
  456. $params[] = $dbhost;
  457. $params[] = ! empty($username) ? $username : null;
  458. $params[] = ! empty($password) ? $password : null;
  459. $params[] = isset($this->dsn['charset']) ? $this->dsn['charset'] : null;
  460. $params[] = isset($this->dsn['buffers']) ? $this->dsn['buffers'] : null;
  461. $params[] = isset($this->dsn['dialect']) ? $this->dsn['dialect'] : null;
  462. $params[] = isset($this->dsn['role']) ? $this->dsn['role'] : null;
  463. $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
  464. $connection = @call_user_func_array($connect_function, $params);
  465. if ($connection <= 0)
  466. {
  467. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, 'unable to establish a connection', __FUNCTION__);
  468. }
  469. if (empty($this->dsn['disable_iso_date']))
  470. {
  471. if (function_exists('ibase_timefmt'))
  472. {
  473. @ibase_timefmt("%Y-%m-%d %H:%M:%S", IBASE_TIMESTAMP);
  474. @ibase_timefmt("%Y-%m-%d", IBASE_DATE);
  475. }
  476. else
  477. {
  478. @ini_set("ibase.timestampformat", "%Y-%m-%d %H:%M:%S");
  479. //@ini_set("ibase.timeformat", "%H:%M:%S");
  480. @ini_set("ibase.dateformat", "%Y-%m-%d");
  481. }
  482. }
  483. return $connection;
  484. }
  485. // }}}
  486. // {{{ connect()
  487. /**
  488. * Connect to the database
  489. *
  490. * @return true on success, MDB2 Error Object on failure
  491. * @access public
  492. */
  493. function connect()
  494. {
  495. $database_file = $this->_getDatabaseFile($this->database_name);
  496. if (is_resource($this->connection))
  497. {
  498. //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
  499. if (MDB2 :: areEquals($this->connected_dsn, $this->dsn) && $this->connected_database_name == $database_file && $this->opened_persistent == $this->options['persistent'])
  500. {
  501. return MDB2_OK;
  502. }
  503. $this->disconnect(false);
  504. }
  505. if (empty($this->database_name))
  506. {
  507. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, 'unable to establish a connection', __FUNCTION__);
  508. }
  509. $connection = $this->_doConnect($this->dsn['username'], $this->dsn['password'], $this->database_name, $this->options['persistent']);
  510. if (PEAR :: isError($connection))
  511. {
  512. return $connection;
  513. }
  514. $this->connection = & $connection;
  515. $this->connected_dsn = $this->dsn;
  516. $this->connected_database_name = $database_file;
  517. $this->opened_persistent = $this->options['persistent'];
  518. $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
  519. $this->supported['limit_queries'] = ($this->dbsyntax == 'firebird') ? true : 'emulated';
  520. return MDB2_OK;
  521. }
  522. // }}}
  523. // {{{ databaseExists()
  524. /**
  525. * check if given database name is exists?
  526. *
  527. * @param string $name name of the database that should be checked
  528. *
  529. * @return mixed true/false on success, a MDB2 error on failure
  530. * @access public
  531. */
  532. function databaseExists($name)
  533. {
  534. $database_file = $this->_getDatabaseFile($name);
  535. $result = file_exists($database_file);
  536. return $result;
  537. }
  538. // }}}
  539. // {{{ disconnect()
  540. /**
  541. * Log out and disconnect from the database.
  542. *
  543. * @param boolean $force if the disconnect should be forced even if the
  544. * connection is opened persistently
  545. * @return mixed true on success, false if not connected and error
  546. * object on error
  547. * @access public
  548. */
  549. function disconnect($force = true)
  550. {
  551. if (is_resource($this->connection))
  552. {
  553. if ($this->in_transaction)
  554. {
  555. $dsn = $this->dsn;
  556. $database_name = $this->database_name;
  557. $persistent = $this->options['persistent'];
  558. $this->dsn = $this->connected_dsn;
  559. $this->database_name = $this->connected_database_name;
  560. $this->options['persistent'] = $this->opened_persistent;
  561. $this->rollback();
  562. $this->dsn = $dsn;
  563. $this->database_name = $database_name;
  564. $this->options['persistent'] = $persistent;
  565. }
  566. if (! $this->opened_persistent || $force)
  567. {
  568. $ok = @ibase_close($this->connection);
  569. if (! $ok)
  570. {
  571. return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED, null, null, null, __FUNCTION__);
  572. }
  573. }
  574. }
  575. else
  576. {
  577. return false;
  578. }
  579. return parent :: disconnect($force);
  580. }
  581. // }}}
  582. // {{{ standaloneQuery()
  583. /**
  584. * execute a query as DBA
  585. *
  586. * @param string $query the SQL query
  587. * @param mixed $types array that contains the types of the columns in
  588. * the result set
  589. * @param boolean $is_manip if the query is a manipulation query
  590. * @return mixed MDB2_OK on success, a MDB2 error on failure
  591. * @access public
  592. */
  593. function &standaloneQuery($query, $types = null, $is_manip = false)
  594. {
  595. $user = $this->options['DBA_username'] ? $this->options['DBA_username'] : $this->dsn['username'];
  596. $pass = $this->options['DBA_password'] ? $this->options['DBA_password'] : $this->dsn['password'];
  597. $connection = $this->_doConnect($user, $pass, $this->database_name, $this->options['persistent']);
  598. if (PEAR :: isError($connection))
  599. {
  600. return $connection;
  601. }
  602. $offset = $this->offset;
  603. $limit = $this->limit;
  604. $this->offset = $this->limit = 0;
  605. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  606. $result = & $this->_doQuery($query, $is_manip, $connection);
  607. if (! PEAR :: isError($result))
  608. {
  609. $result = $this->_affectedRows($connection, $result);
  610. }
  611. @mysql_close($connection);
  612. return $result;
  613. }
  614. // }}}
  615. // {{{ _doQuery()
  616. /**
  617. * Execute a query
  618. * @param string $query query
  619. * @param boolean $is_manip if the query is a manipulation query
  620. * @param resource $connection
  621. * @param string $database_name
  622. * @return result or error object
  623. * @access protected
  624. */
  625. function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  626. {
  627. $this->last_query = $query;
  628. $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  629. if ($result)
  630. {
  631. if (PEAR :: isError($result))
  632. {
  633. return $result;
  634. }
  635. $query = $result;
  636. }
  637. if ($this->getOption('disable_query'))
  638. {
  639. if ($is_manip)
  640. {
  641. return 0;
  642. }
  643. return null;
  644. }
  645. if (is_null($connection))
  646. {
  647. $connection = $this->getConnection();
  648. if (PEAR :: isError($connection))
  649. {
  650. return $connection;
  651. }
  652. }
  653. $result = @ibase_query($connection, $query);
  654. if ($result === false)
  655. {
  656. $err = & $this->raiseError(null, null, null, 'Could not execute statement', __FUNCTION__);
  657. return $err;
  658. }
  659. $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
  660. return $result;
  661. }
  662. // }}}
  663. // {{{ _affectedRows()
  664. /**
  665. * Returns the number of rows affected
  666. *
  667. * @param resource $result
  668. * @param resource $connection
  669. * @return mixed MDB2 Error Object or the number of rows affected
  670. * @access private
  671. */
  672. function _affectedRows($connection, $result = null)
  673. {
  674. if (is_null($connection))
  675. {
  676. $connection = $this->getConnection();
  677. if (PEAR :: isError($connection))
  678. {
  679. return $connection;
  680. }
  681. }
  682. return (function_exists('ibase_affected_rows') ? @ibase_affected_rows($connection) : 0);
  683. }
  684. // }}}
  685. // {{{ _modifyQuery()
  686. /**
  687. * Changes a query string for various DBMS specific reasons
  688. *
  689. * @param string $query query to modify
  690. * @param boolean $is_manip if it is a DML query
  691. * @param integer $limit limit the number of rows
  692. * @param integer $offset start reading from given offset
  693. * @return string modified query
  694. * @access protected
  695. */
  696. function _modifyQuery($query, $is_manip, $limit, $offset)
  697. {
  698. if ($limit > 0 && $this->supports('limit_queries') === true)
  699. {
  700. $query = preg_replace('/^([\s(])*SELECT(?!\s*FIRST\s*\d+)/i', "SELECT FIRST $limit SKIP $offset", $query);
  701. }
  702. return $query;
  703. }
  704. // }}}
  705. // {{{ getServerVersion()
  706. /**
  707. * return version information about the server
  708. *
  709. * @param bool $native determines if the raw version string should be returned
  710. * @return mixed array/string with version information or MDB2 error object
  711. * @access public
  712. */
  713. function getServerVersion($native = false)
  714. {
  715. $server_info = false;
  716. if ($this->connected_server_info)
  717. {
  718. $server_info = $this->connected_server_info;
  719. }
  720. elseif ($this->options['server_version'])
  721. {
  722. $server_info = $this->options['server_version'];
  723. }
  724. else
  725. {
  726. $username = $this->options['DBA_username'] ? $this->options['DBA_username'] : $this->dsn['username'];
  727. $password = $this->options['DBA_password'] ? $this->options['DBA_password'] : $this->dsn['password'];
  728. $ibserv = @ibase_service_attach($this->dsn['hostspec'], $username, $password);
  729. $server_info = @ibase_server_info($ibserv, IBASE_SVC_SERVER_VERSION);
  730. @ibase_service_detach($ibserv);
  731. }
  732. if (! $server_info)
  733. {
  734. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'Requires either "server_version" or "DBA_username"/"DBA_password" option', __FUNCTION__);
  735. }
  736. // cache server_info
  737. $this->connected_server_info = $server_info;
  738. if (! $native)
  739. {
  740. //WI-V1.5.3.4854 Firebird 1.5
  741. //WI-T2.1.0.16780 Firebird 2.1 Beta 2
  742. if (! preg_match('/-[VT]([\d\.]*)/', $server_info, $matches))
  743. {
  744. return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'Could not parse version information:' . $server_info, __FUNCTION__);
  745. }
  746. $tmp = explode('.', $matches[1], 4);
  747. $server_info = array('major' => isset($tmp[0]) ? $tmp[0] : null, 'minor' => isset($tmp[1]) ? $tmp[1] : null,
  748. 'patch' => isset($tmp[2]) ? $tmp[2] : null, 'extra' => isset($tmp[3]) ? $tmp[3] : null,
  749. 'native' => $server_info);
  750. }
  751. return $server_info;
  752. }
  753. // }}}
  754. // {{{ prepare()
  755. /**
  756. * Prepares a query for multiple execution with execute().
  757. * With some database backends, this is emulated.
  758. * prepare() requires a generic query as string like
  759. * 'INSERT INTO numbers VALUES(?,?)' or
  760. * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  761. * The ? and :name and are placeholders which can be set using
  762. * bindParam() and the query can be sent off using the execute() method.
  763. * The allowed format for :name can be set with the 'bindname_format' option.
  764. *
  765. * @param string $query the query to prepare
  766. * @param mixed $types array that contains the types of the placeholders
  767. * @param mixed $result_types array that contains the types of the columns in
  768. * the result set or MDB2_PREPARE_RESULT, if set to
  769. * MDB2_PREPARE_MANIP the query is handled as a manipulation query
  770. * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
  771. * @return mixed resource handle for the prepared query on success, a MDB2
  772. * error on failure
  773. * @access public
  774. * @see bindParam, execute
  775. */
  776. function &prepare($query, $types = null, $result_types = null, $lobs = array())
  777. {
  778. if ($this->options['emulate_prepared'])
  779. {
  780. $obj = & parent :: prepare($query, $types, $result_types, $lobs);
  781. return $obj;
  782. }
  783. $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  784. $offset = $this->offset;
  785. $limit = $this->limit;
  786. $this->offset = $this->limit = 0;
  787. $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
  788. if ($result)
  789. {
  790. if (PEAR :: isError($result))
  791. {
  792. return $result;
  793. }
  794. $query = $result;
  795. }
  796. $placeholder_type_guess = $placeholder_type = null;
  797. $question = '?';
  798. $colon = ':';
  799. $positions = array();
  800. $position = 0;
  801. while ($position < strlen($query))
  802. {
  803. $q_position = strpos($query, $question, $position);
  804. $c_position = strpos($query, $colon, $position);
  805. if ($q_position && $c_position)
  806. {
  807. $p_position = min($q_position, $c_position);
  808. }
  809. elseif ($q_position)
  810. {
  811. $p_position = $q_position;
  812. }
  813. elseif ($c_position)
  814. {
  815. $p_position = $c_position;
  816. }
  817. else
  818. {
  819. break;
  820. }
  821. if (is_null($placeholder_type))
  822. {
  823. $placeholder_type_guess = $query[$p_position];
  824. }
  825. $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
  826. if (PEAR :: isError($new_pos))
  827. {
  828. return $new_pos;
  829. }
  830. if ($new_pos != $position)
  831. {
  832. $position = $new_pos;
  833. continue; //evaluate again starting from the new position
  834. }
  835. if ($query[$position] == $placeholder_type_guess)
  836. {
  837. if (is_null($placeholder_type))
  838. {
  839. $placeholder_type = $query[$p_position];
  840. $question = $colon = $placeholder_type;
  841. }
  842. if ($placeholder_type == ':')
  843. {
  844. $regexp = '/^.{' . ($position + 1) . '}(' . $this->options['bindname_format'] . ').*$/s';
  845. $parameter = preg_replace($regexp, '\\1', $query);
  846. if ($parameter === '')
  847. {
  848. $err = & $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'named parameter name must match "bindname_format" option', __FUNCTION__);
  849. return $err;
  850. }
  851. $positions[] = $parameter;
  852. $query = substr_replace($query, '?', $position, strlen($parameter) + 1);
  853. }
  854. else
  855. {
  856. $positions[] = count($positions);
  857. }
  858. $position = $p_position + 1;
  859. }
  860. else
  861. {
  862. $position = $p_position;
  863. }
  864. }
  865. $connection = $this->getConnection();
  866. if (PEAR :: isError($connection))
  867. {
  868. return $connection;
  869. }
  870. $statement = @ibase_prepare($connection, $query);
  871. if (! $statement)
  872. {
  873. $err = & $this->raiseError(null, null, null, 'Could not create statement', __FUNCTION__);
  874. return $err;
  875. }
  876. $class_name = 'MDB2_Statement_' . $this->phptype;
  877. $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  878. $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
  879. return $obj;
  880. }
  881. // }}}
  882. // {{{ getSequenceName()
  883. /**
  884. * adds sequence name formatting to a sequence name
  885. *
  886. * @param string $sqn name of the sequence
  887. * @return string formatted sequence name
  888. * @access public
  889. */
  890. function getSequenceName($sqn)
  891. {
  892. return strtoupper(parent :: getSequenceName($sqn));
  893. }
  894. // }}}
  895. // {{{ nextID()
  896. /**
  897. * Returns the next free id of a sequence
  898. *
  899. * @param string $seq_name name of the sequence
  900. * @param boolean $ondemand when true the sequence is
  901. * automatic created, if it
  902. * not exists
  903. * @return mixed MDB2 Error Object or id
  904. * @access public
  905. */
  906. function nextID($seq_name, $ondemand = true)
  907. {
  908. $sequence_name = $this->getSequenceName($seq_name);
  909. $query = 'SELECT GEN_ID(' . $sequence_name . ', 1) as the_value FROM RDB$DATABASE';
  910. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  911. $this->expectError('*');
  912. $result = $this->queryOne($query, 'integer');
  913. $this->popExpect();
  914. $this->popErrorHandling();
  915. if (PEAR :: isError($result))
  916. {
  917. if ($ondemand)
  918. {
  919. $this->loadModule('Manager', null, true);
  920. $result = $this->manager->createSequence($seq_name);
  921. if (PEAR :: isError($result))
  922. {
  923. return $this->raiseError($result, null, null, 'on demand sequence could not be created', __FUNCTION__);
  924. }
  925. else
  926. {
  927. return $this->nextID($seq_name, false);
  928. }
  929. }
  930. }
  931. return $result;
  932. }
  933. // }}}
  934. // {{{ lastInsertID()
  935. /**
  936. * Returns the autoincrement ID if supported or $id or fetches the current
  937. * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  938. *
  939. * @param string $table name of the table into which a new row was inserted
  940. * @param string $field name of the field into which a new row was inserted
  941. * @return mixed MDB2 Error Object or id
  942. * @access public
  943. */
  944. function lastInsertID($table = null, $field = null)
  945. {
  946. $seq = $table . (empty($field) ? '' : '_' . $field);
  947. return $this->currID($seq);
  948. }
  949. // }}}
  950. // {{{ currID()
  951. /**
  952. * Returns the current id of a sequence
  953. *
  954. * @param string $seq_name name of the sequence
  955. * @return mixed MDB2 Error Object or id
  956. * @access public
  957. */
  958. function currID($seq_name)
  959. {
  960. $sequence_name = $this->getSequenceName($seq_name);
  961. $query = 'SELECT GEN_ID(' . $sequence_name . ', 0) as the_value FROM RDB$DATABASE';
  962. $value = $this->queryOne($query);
  963. if (PEAR :: isError($value))
  964. {
  965. return $this->raiseError($value, null, null, 'Unable to select from ' . $seq_name, __FUNCTION__);
  966. }
  967. if (! is_numeric($value))
  968. {
  969. return $this->raiseError(MDB2_ERROR, null, null, 'could not find value in sequence table', __FUNCTION__);
  970. }
  971. return $value;
  972. }
  973. // }}}
  974. }
  975. /**
  976. * MDB2 FireBird/InterBase result driver
  977. *
  978. * @package MDB2
  979. * @category Database
  980. * @author Lorenzo Alberton <l.alberton@quipo.it>
  981. */
  982. class MDB2_Result_ibase extends MDB2_Result_Common
  983. {
  984. // {{{ _skipLimitOffset()
  985. /**
  986. * Skip the first row of a result set.
  987. *
  988. * @param resource $result
  989. * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  990. * @access protected
  991. */
  992. function _skipLimitOffset()
  993. {
  994. if ($this->db->supports('limit_queries') === true)
  995. {
  996. return true;
  997. }
  998. if ($this->limit)
  999. {
  1000. if ($this->rownum > $this->limit)
  1001. {
  1002. return false;
  1003. }
  1004. }
  1005. if ($this->offset)
  1006. {
  1007. while ($this->offset_count < $this->offset)
  1008. {
  1009. ++ $this->offset_count;
  1010. if (! is_array(@ibase_fetch_row($this->result)))
  1011. {
  1012. $this->offset_count = $this->offset;
  1013. return false;
  1014. }
  1015. }
  1016. }
  1017. return true;
  1018. }
  1019. // }}}
  1020. // {{{ fetchRow()
  1021. /**
  1022. * Fetch a row and insert the data into an existing array.
  1023. *
  1024. * @param int $fetchmode how the array data should be indexed
  1025. * @param int $rownum number of the row where the data can be found
  1026. * @return int data array on success, a MDB2 error on failure
  1027. * @access public
  1028. */
  1029. function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  1030. {
  1031. if ($this->result === true)
  1032. {
  1033. //query successfully executed, but without results...
  1034. $null = null;
  1035. return $null;
  1036. }
  1037. if (! $this->_skipLimitOffset())
  1038. {
  1039. $null = null;
  1040. return $null;
  1041. }
  1042. if (! is_null($rownum))
  1043. {
  1044. $seek = $this->seek($rownum);
  1045. if (PEAR :: isError($seek))
  1046. {
  1047. return $seek;
  1048. }
  1049. }
  1050. if ($fetchmode == MDB2_FETCHMODE_DEFAULT)
  1051. {
  1052. $fetchmode = $this->db->fetchmode;
  1053. }
  1054. if ($fetchmode & MDB2_FETCHMODE_ASSOC)
  1055. {
  1056. $row = @ibase_fetch_assoc($this->result);
  1057. if (is_array($row) && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  1058. {
  1059. $row = array_change_key_case($row, $this->db->options['field_case']);
  1060. }
  1061. }
  1062. else
  1063. {
  1064. $row = @ibase_fetch_row($this->result);
  1065. }
  1066. if (! $row)
  1067. {
  1068. if ($this->result === false)
  1069. {
  1070. $err = & $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__);
  1071. return $err;
  1072. }
  1073. $null = null;
  1074. return $null;
  1075. }
  1076. $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
  1077. $rtrim = false;
  1078. if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM)
  1079. {
  1080. if (empty($this->types))
  1081. {
  1082. $mode += MDB2_PORTABILITY_RTRIM;
  1083. }
  1084. else
  1085. {
  1086. $rtrim = true;
  1087. }
  1088. }
  1089. if ($mode)
  1090. {
  1091. $this->db->_fixResultArrayValues($row, $mode);
  1092. }
  1093. if (! ($fetchmode & MDB2_FETCHMODE_ASSOC) && ! empty($this->types))
  1094. {
  1095. $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
  1096. }
  1097. elseif (($fetchmode & MDB2_FETCHMODE_ASSOC) && ! empty($this->types_assoc))
  1098. {
  1099. $row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim);
  1100. }
  1101. if (! empty($this->values))
  1102. {
  1103. $this->_assignBindColumns($row);
  1104. }
  1105. if ($fetchmode === MDB2_FETCHMODE_OBJECT)
  1106. {
  1107. $object_class = $this->db->options['fetch_class'];
  1108. if ($object_class == 'stdClass')
  1109. {
  1110. $row = (object) $row;
  1111. }
  1112. else
  1113. {
  1114. $row = &new $object_class($row);
  1115. }
  1116. }
  1117. ++ $this->rownum;
  1118. return $row;
  1119. }
  1120. // }}}
  1121. // {{{ _getColumnNames()
  1122. /**
  1123. * Retrieve the names of columns returned by the DBMS in a query result.
  1124. *
  1125. * @return mixed Array variable that holds the names of columns as keys
  1126. * or an MDB2 error on failure.
  1127. * Some DBMS may not return any columns when the result set
  1128. * does not contain any rows.
  1129. * @access private
  1130. */
  1131. function _getColumnNames()
  1132. {
  1133. $columns = array();
  1134. $numcols = $this->numCols();
  1135. if (PEAR :: isError($numcols))
  1136. {
  1137. return $numcols;
  1138. }
  1139. for($column = 0; $column < $numcols; $column ++)
  1140. {
  1141. $column_info = @ibase_field_info($this->result, $column);
  1142. $columns[$column_info['alias']] = $column;
  1143. }
  1144. if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  1145. {
  1146. $columns = array_change_key_case($columns, $this->db->options['field_case']);
  1147. }
  1148. return $columns;
  1149. }
  1150. // }}}
  1151. // {{{ numCols()
  1152. /**
  1153. * Count the number of columns returned by the DBMS in a query result.
  1154. *
  1155. * @return mixed integer value with the number of columns, a MDB2 error
  1156. * on failure
  1157. * @access public
  1158. */
  1159. function numCols()
  1160. {
  1161. if ($this->result === true)
  1162. {
  1163. //query successfully executed, but without results...
  1164. return 0;
  1165. }
  1166. if (! is_resource($this->result))
  1167. {
  1168. return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'numCols(): not a valid ibase resource', __FUNCTION__);
  1169. }
  1170. $cols = @ibase_num_fields($this->result);
  1171. if (is_null($cols))
  1172. {
  1173. if ($this->result === false)
  1174. {
  1175. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__);
  1176. }
  1177. elseif (is_null($this->result))
  1178. {
  1179. return count($this->types);
  1180. }
  1181. return $this->db->raiseError(null, null, null, 'Could not get column count', __FUNCTION__);
  1182. }
  1183. return $cols;
  1184. }
  1185. // }}}
  1186. // {{{ free()
  1187. /**
  1188. * Free the internal resources associated with $result.
  1189. *
  1190. * @return boolean true on success, false if $result is invalid
  1191. * @access public
  1192. */
  1193. function free()
  1194. {
  1195. if (is_resource($this->result) && $this->db->connection)
  1196. {
  1197. $free = @ibase_free_result($this->result);
  1198. if ($free === false)
  1199. {
  1200. return $this->db->raiseError(null, null, null, 'Could not free result', __FUNCTION__);
  1201. }
  1202. }
  1203. $this->result = false;
  1204. return MDB2_OK;
  1205. }
  1206. // }}}
  1207. }
  1208. /**
  1209. * MDB2 FireBird/InterBase buffered result driver
  1210. *
  1211. * @package MDB2
  1212. * @category Database
  1213. * @author Lorenzo Alberton <l.alberton@quipo.it>
  1214. */
  1215. class MDB2_BufferedResult_ibase extends MDB2_Result_ibase
  1216. {
  1217. // {{{ class vars
  1218. var $buffer;
  1219. var $buffer_rownum = - 1;
  1220. // }}}
  1221. // {{{ _fillBuffer()
  1222. /**
  1223. * Fill the row buffer
  1224. *
  1225. * @param int $rownum row number upto which the buffer should be filled
  1226. * if the row number is null all rows are ready into the buffer
  1227. * @return boolean true on success, false on failure
  1228. * @access protected
  1229. */
  1230. function _fillBuffer($rownum = null)
  1231. {
  1232. if (isset($this->buffer) && is_array($this->buffer))
  1233. {
  1234. if (is_null($rownum))
  1235. {
  1236. if (! end($this->buffer))
  1237. {
  1238. return false;
  1239. }
  1240. }
  1241. elseif (isset($this->buffer[$rownum]))
  1242. {
  1243. return (bool) $this->buffer[$rownum];
  1244. }
  1245. }
  1246. if (! $this->_skipLimitOffset())
  1247. {
  1248. return false;
  1249. }
  1250. $buffer = true;
  1251. while ((is_null($rownum) || $this->buffer_rownum < $rownum) && (! $this->limit || $this->buffer_rownum < $this->limit) && ($buffer = @ibase_fetch_row($this->result)))
  1252. {
  1253. ++ $this->buffer_rownum;
  1254. $this->buffer[$this->buffer_rownum] = $buffer;
  1255. }
  1256. if (! $buffer)
  1257. {
  1258. ++ $this->buffer_rownum;
  1259. $this->buffer[$this->buffer_rownum] = false;
  1260. return false;
  1261. }
  1262. elseif ($this->limit && $this->buffer_rownum >= $this->limit)
  1263. {
  1264. ++ $this->buffer_rownum;
  1265. $this->buffer[$this->buffer_rownum] = false;
  1266. }
  1267. return true;
  1268. }
  1269. // }}}
  1270. // {{{ fetchRow()
  1271. /**
  1272. * Fetch a row and insert the data into an existing array.
  1273. *
  1274. * @param int $fetchmode how the array data should be indexed
  1275. * @param int $rownum number of the row where the data can be found
  1276. * @return int data array on success, a MDB2 error on failure
  1277. * @access public
  1278. */
  1279. function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  1280. {
  1281. if (

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