PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

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

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