PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/chamilo/chamilo/
PHP | 1635 lines | 1179 code | 93 blank | 363 comment | 239 complexity | e84ed14c6eecbebef99b87906b36028e MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT

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

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