PageRenderTime 22ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/phpBB/includes/db/oracle.php

https://github.com/naderman/phpbb-orchestra
PHP | 771 lines | 547 code | 69 blank | 155 comment | 48 complexity | d819a427769446bba4258e86d24bfe5d MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * @package dbal
  5. * @version $Id$
  6. * @copyright (c) 2005 phpBB Group
  7. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8. *
  9. */
  10. /**
  11. * @ignore
  12. */
  13. if (!defined('IN_PHPBB'))
  14. {
  15. exit;
  16. }
  17. include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
  18. /**
  19. * Oracle Database Abstraction Layer
  20. * @package dbal
  21. */
  22. class dbal_oracle extends dbal
  23. {
  24. var $last_query_text = '';
  25. /**
  26. * Connect to server
  27. */
  28. function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
  29. {
  30. $this->persistency = $persistency;
  31. $this->user = $sqluser;
  32. $this->server = $sqlserver . (($port) ? ':' . $port : '');
  33. $this->dbname = $database;
  34. $connect = $database;
  35. // support for "easy connect naming"
  36. if ($sqlserver !== '' && $sqlserver !== '/')
  37. {
  38. if (substr($sqlserver, -1, 1) == '/')
  39. {
  40. $sqlserver == substr($sqlserver, 0, -1);
  41. }
  42. $connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database;
  43. }
  44. $this->db_connect_id = ($new_link) ? @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8') : (($this->persistency) ? @ociplogon($this->user, $sqlpassword, $connect, 'UTF8') : @ocilogon($this->user, $sqlpassword, $connect, 'UTF8'));
  45. return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
  46. }
  47. /**
  48. * Version information about used database
  49. * @param bool $raw if true, only return the fetched sql_server_version
  50. * @param bool $use_cache forced to false for Oracle
  51. * @return string sql server version
  52. */
  53. function sql_server_info($raw = false, $use_cache = true)
  54. {
  55. /**
  56. * force $use_cache false. I didn't research why the caching code below is commented out
  57. * but I assume its because the Oracle extension provides a direct method to access it
  58. * without a query.
  59. */
  60. $use_cache = false;
  61. /*
  62. global $cache;
  63. if (empty($cache) || ($this->sql_server_version = $cache->get('oracle_version')) === false)
  64. {
  65. $result = @ociparse($this->db_connect_id, 'SELECT * FROM v$version WHERE banner LIKE \'Oracle%\'');
  66. @ociexecute($result, OCI_DEFAULT);
  67. @ocicommit($this->db_connect_id);
  68. $row = array();
  69. @ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS);
  70. @ocifreestatement($result);
  71. $this->sql_server_version = trim($row['BANNER']);
  72. $cache->put('oracle_version', $this->sql_server_version);
  73. }
  74. */
  75. $this->sql_server_version = @ociserverversion($this->db_connect_id);
  76. return $this->sql_server_version;
  77. }
  78. /**
  79. * SQL Transaction
  80. * @access private
  81. */
  82. function _sql_transaction($status = 'begin')
  83. {
  84. switch ($status)
  85. {
  86. case 'begin':
  87. return true;
  88. break;
  89. case 'commit':
  90. return @ocicommit($this->db_connect_id);
  91. break;
  92. case 'rollback':
  93. return @ocirollback($this->db_connect_id);
  94. break;
  95. }
  96. return true;
  97. }
  98. /**
  99. * Oracle specific code to handle the fact that it does not compare columns properly
  100. * @access private
  101. */
  102. function _rewrite_col_compare($args)
  103. {
  104. if (sizeof($args) == 4)
  105. {
  106. if ($args[2] == '=')
  107. {
  108. return '(' . $args[0] . ' OR (' . $args[1] . ' is NULL AND ' . $args[3] . ' is NULL))';
  109. }
  110. else if ($args[2] == '<>')
  111. {
  112. // really just a fancy way of saying foo <> bar or (foo is NULL XOR bar is NULL) but SQL has no XOR :P
  113. return '(' . $args[0] . ' OR ((' . $args[1] . ' is NULL AND ' . $args[3] . ' is NOT NULL) OR (' . $args[1] . ' is NOT NULL AND ' . $args[3] . ' is NULL)))';
  114. }
  115. }
  116. else
  117. {
  118. return $this->_rewrite_where($args[0]);
  119. }
  120. }
  121. /**
  122. * Oracle specific code to handle it's lack of sanity
  123. * @access private
  124. */
  125. function _rewrite_where($where_clause)
  126. {
  127. preg_match_all('/\s*(AND|OR)?\s*([\w_.()]++)\s*(?:(=|<[=>]?|>=?|LIKE)\s*((?>\'(?>[^\']++|\'\')*+\'|[\d-.()]+))|((NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))/', $where_clause, $result, PREG_SET_ORDER);
  128. $out = '';
  129. foreach ($result as $val)
  130. {
  131. if (!isset($val[5]))
  132. {
  133. if ($val[4] !== "''")
  134. {
  135. $out .= $val[0];
  136. }
  137. else
  138. {
  139. $out .= ' ' . $val[1] . ' ' . $val[2];
  140. if ($val[3] == '=')
  141. {
  142. $out .= ' is NULL';
  143. }
  144. else if ($val[3] == '<>')
  145. {
  146. $out .= ' is NOT NULL';
  147. }
  148. }
  149. }
  150. else
  151. {
  152. $in_clause = array();
  153. $sub_exp = substr($val[5], strpos($val[5], '(') + 1, -1);
  154. $extra = false;
  155. preg_match_all('/\'(?>[^\']++|\'\')*+\'|[\d-.]++/', $sub_exp, $sub_vals, PREG_PATTERN_ORDER);
  156. $i = 0;
  157. foreach ($sub_vals[0] as $sub_val)
  158. {
  159. // two things:
  160. // 1) This determines if an empty string was in the IN clausing, making us turn it into a NULL comparison
  161. // 2) This fixes the 1000 list limit that Oracle has (ORA-01795)
  162. if ($sub_val !== "''")
  163. {
  164. $in_clause[(int) $i++/1000][] = $sub_val;
  165. }
  166. else
  167. {
  168. $extra = true;
  169. }
  170. }
  171. if (!$extra && $i < 1000)
  172. {
  173. $out .= $val[0];
  174. }
  175. else
  176. {
  177. $out .= ' ' . $val[1] . '(';
  178. $in_array = array();
  179. // constuct each IN() clause
  180. foreach ($in_clause as $in_values)
  181. {
  182. $in_array[] = $val[2] . ' ' . (isset($val[6]) ? $val[6] : '') . 'IN(' . implode(', ', $in_values) . ')';
  183. }
  184. // Join the IN() clauses against a few ORs (IN is just a nicer OR anyway)
  185. $out .= implode(' OR ', $in_array);
  186. // handle the empty string case
  187. if ($extra)
  188. {
  189. $out .= ' OR ' . $val[2] . ' is ' . (isset($val[6]) ? $val[6] : '') . 'NULL';
  190. }
  191. $out .= ')';
  192. unset($in_array, $in_clause);
  193. }
  194. }
  195. }
  196. return $out;
  197. }
  198. /**
  199. * Base query method
  200. *
  201. * @param string $query Contains the SQL query which shall be executed
  202. * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
  203. * @return mixed When casted to bool the returned value returns true on success and false on failure
  204. *
  205. * @access public
  206. */
  207. function sql_query($query = '', $cache_ttl = 0)
  208. {
  209. if ($query != '')
  210. {
  211. global $cache;
  212. // EXPLAIN only in extra debug mode
  213. if (defined('DEBUG_EXTRA'))
  214. {
  215. $this->sql_report('start', $query);
  216. }
  217. $this->last_query_text = $query;
  218. $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
  219. $this->sql_add_num_queries($this->query_result);
  220. if ($this->query_result === false)
  221. {
  222. $in_transaction = false;
  223. if (!$this->transaction)
  224. {
  225. $this->sql_transaction('begin');
  226. }
  227. else
  228. {
  229. $in_transaction = true;
  230. }
  231. $array = array();
  232. // We overcome Oracle's 4000 char limit by binding vars
  233. if (strlen($query) > 4000)
  234. {
  235. if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/sU', $query, $regs))
  236. {
  237. if (strlen($regs[3]) > 4000)
  238. {
  239. $cols = explode(', ', $regs[2]);
  240. preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER);
  241. /* The code inside this comment block breaks clob handling, but does allow the
  242. database restore script to work. If you want to allow no posts longer than 4KB
  243. and/or need the db restore script, uncomment this.
  244. if (sizeof($cols) !== sizeof($vals))
  245. {
  246. // Try to replace some common data we know is from our restore script or from other sources
  247. $regs[3] = str_replace("'||chr(47)||'", '/', $regs[3]);
  248. $_vals = explode(', ', $regs[3]);
  249. $vals = array();
  250. $is_in_val = false;
  251. $i = 0;
  252. $string = '';
  253. foreach ($_vals as $value)
  254. {
  255. if (strpos($value, "'") === false && !$is_in_val)
  256. {
  257. $vals[$i++] = $value;
  258. continue;
  259. }
  260. if (substr($value, -1) === "'")
  261. {
  262. $vals[$i] = $string . (($is_in_val) ? ', ' : '') . $value;
  263. $string = '';
  264. $is_in_val = false;
  265. if ($vals[$i][0] !== "'")
  266. {
  267. $vals[$i] = "''" . $vals[$i];
  268. }
  269. $i++;
  270. continue;
  271. }
  272. else
  273. {
  274. $string .= (($is_in_val) ? ', ' : '') . $value;
  275. $is_in_val = true;
  276. }
  277. }
  278. if ($string)
  279. {
  280. // New value if cols != value
  281. $vals[(sizeof($cols) !== sizeof($vals)) ? $i : $i - 1] .= $string;
  282. }
  283. $vals = array(0 => $vals);
  284. }
  285. */
  286. $inserts = $vals[0];
  287. unset($vals);
  288. foreach ($inserts as $key => $value)
  289. {
  290. if (!empty($value) && $value[0] === "'" && strlen($value) > 4002) // check to see if this thing is greater than the max + 'x2
  291. {
  292. $inserts[$key] = ':' . strtoupper($cols[$key]);
  293. $array[$inserts[$key]] = str_replace("''", "'", substr($value, 1, -1));
  294. }
  295. }
  296. $query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')';
  297. }
  298. }
  299. else if (preg_match_all('/^(UPDATE [\\w_]++\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data, PREG_SET_ORDER))
  300. {
  301. if (strlen($data[0][2]) > 4000)
  302. {
  303. $update = $data[0][1];
  304. $where = $data[0][3];
  305. preg_match_all('/([\\w_]++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[0][2], $temp, PREG_SET_ORDER);
  306. unset($data);
  307. $cols = array();
  308. foreach ($temp as $value)
  309. {
  310. if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 4002) // check to see if this thing is greater than the max + 'x2
  311. {
  312. $cols[] = $value[1] . '=:' . strtoupper($value[1]);
  313. $array[$value[1]] = str_replace("''", "'", substr($value[2], 1, -1));
  314. }
  315. else
  316. {
  317. $cols[] = $value[1] . '=' . $value[2];
  318. }
  319. }
  320. $query = $update . implode(', ', $cols) . ' ' . $where;
  321. unset($cols);
  322. }
  323. }
  324. }
  325. switch (substr($query, 0, 6))
  326. {
  327. case 'DELETE':
  328. if (preg_match('/^(DELETE FROM [\w_]++ WHERE)((?:\s*(?:AND|OR)?\s*[\w_]+\s*(?:(?:=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]+)|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))*+)$/', $query, $regs))
  329. {
  330. $query = $regs[1] . $this->_rewrite_where($regs[2]);
  331. unset($regs);
  332. }
  333. break;
  334. case 'UPDATE':
  335. if (preg_match('/^(UPDATE [\\w_]++\\s+SET [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++)(?:, [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++))*+\\s+WHERE)(.*)$/s', $query, $regs))
  336. {
  337. $query = $regs[1] . $this->_rewrite_where($regs[2]);
  338. unset($regs);
  339. }
  340. break;
  341. case 'SELECT':
  342. $query = preg_replace_callback('/([\w_.]++)\s*(?:(=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]++|([\w_.]++))|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]++,? ?)*+\))/', array($this, '_rewrite_col_compare'), $query);
  343. break;
  344. }
  345. $this->query_result = @ociparse($this->db_connect_id, $query);
  346. foreach ($array as $key => $value)
  347. {
  348. @ocibindbyname($this->query_result, $key, $array[$key], -1);
  349. }
  350. $success = @ociexecute($this->query_result, OCI_DEFAULT);
  351. if (!$success)
  352. {
  353. $this->sql_error($query);
  354. $this->query_result = false;
  355. }
  356. else
  357. {
  358. if (!$in_transaction)
  359. {
  360. $this->sql_transaction('commit');
  361. }
  362. }
  363. if (defined('DEBUG_EXTRA'))
  364. {
  365. $this->sql_report('stop', $query);
  366. }
  367. if ($cache_ttl && method_exists($cache, 'sql_save'))
  368. {
  369. $this->open_queries[(int) $this->query_result] = $this->query_result;
  370. $cache->sql_save($query, $this->query_result, $cache_ttl);
  371. }
  372. else if (strpos($query, 'SELECT') === 0 && $this->query_result)
  373. {
  374. $this->open_queries[(int) $this->query_result] = $this->query_result;
  375. }
  376. }
  377. else if (defined('DEBUG_EXTRA'))
  378. {
  379. $this->sql_report('fromcache', $query);
  380. }
  381. }
  382. else
  383. {
  384. return false;
  385. }
  386. return $this->query_result;
  387. }
  388. /**
  389. * Build LIMIT query
  390. */
  391. function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
  392. {
  393. $this->query_result = false;
  394. $query = 'SELECT * FROM (SELECT /*+ FIRST_ROWS */ rownum AS xrownum, a.* FROM (' . $query . ') a WHERE rownum <= ' . ($offset + $total) . ') WHERE xrownum >= ' . $offset;
  395. return $this->sql_query($query, $cache_ttl);
  396. }
  397. /**
  398. * Return number of affected rows
  399. */
  400. function sql_affectedrows()
  401. {
  402. return ($this->query_result) ? @ocirowcount($this->query_result) : false;
  403. }
  404. /**
  405. * Fetch current row
  406. */
  407. function sql_fetchrow($query_id = false)
  408. {
  409. global $cache;
  410. if ($query_id === false)
  411. {
  412. $query_id = $this->query_result;
  413. }
  414. if (isset($cache->sql_rowset[$query_id]))
  415. {
  416. return $cache->sql_fetchrow($query_id);
  417. }
  418. if ($query_id !== false)
  419. {
  420. $row = array();
  421. $result = @ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
  422. if (!$result || !$row)
  423. {
  424. return false;
  425. }
  426. $result_row = array();
  427. foreach ($row as $key => $value)
  428. {
  429. // Oracle treats empty strings as null
  430. if (is_null($value))
  431. {
  432. $value = '';
  433. }
  434. // OCI->CLOB?
  435. if (is_object($value))
  436. {
  437. $value = $value->load();
  438. }
  439. $result_row[strtolower($key)] = $value;
  440. }
  441. return $result_row;
  442. }
  443. return false;
  444. }
  445. /**
  446. * Seek to given row number
  447. * rownum is zero-based
  448. */
  449. function sql_rowseek($rownum, &$query_id)
  450. {
  451. global $cache;
  452. if ($query_id === false)
  453. {
  454. $query_id = $this->query_result;
  455. }
  456. if (isset($cache->sql_rowset[$query_id]))
  457. {
  458. return $cache->sql_rowseek($rownum, $query_id);
  459. }
  460. if ($query_id === false)
  461. {
  462. return false;
  463. }
  464. // Reset internal pointer
  465. @ociexecute($query_id, OCI_DEFAULT);
  466. // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
  467. for ($i = 0; $i < $rownum; $i++)
  468. {
  469. if (!$this->sql_fetchrow($query_id))
  470. {
  471. return false;
  472. }
  473. }
  474. return true;
  475. }
  476. /**
  477. * Get last inserted id after insert statement
  478. */
  479. function sql_nextid()
  480. {
  481. $query_id = $this->query_result;
  482. if ($query_id !== false && $this->last_query_text != '')
  483. {
  484. if (preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename))
  485. {
  486. $query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL';
  487. $stmt = @ociparse($this->db_connect_id, $query);
  488. @ociexecute($stmt, OCI_DEFAULT);
  489. $temp_result = @ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS);
  490. @ocifreestatement($stmt);
  491. if ($temp_result)
  492. {
  493. return $temp_array['CURRVAL'];
  494. }
  495. else
  496. {
  497. return false;
  498. }
  499. }
  500. }
  501. return false;
  502. }
  503. /**
  504. * Free sql result
  505. */
  506. function sql_freeresult($query_id = false)
  507. {
  508. global $cache;
  509. if ($query_id === false)
  510. {
  511. $query_id = $this->query_result;
  512. }
  513. if (isset($cache->sql_rowset[$query_id]))
  514. {
  515. return $cache->sql_freeresult($query_id);
  516. }
  517. if (isset($this->open_queries[(int) $query_id]))
  518. {
  519. unset($this->open_queries[(int) $query_id]);
  520. return @ocifreestatement($query_id);
  521. }
  522. return false;
  523. }
  524. /**
  525. * Escape string used in sql query
  526. */
  527. function sql_escape($msg)
  528. {
  529. return str_replace(array("'", "\0"), array("''", ''), $msg);
  530. }
  531. /**
  532. * Build LIKE expression
  533. * @access private
  534. */
  535. function _sql_like_expression($expression)
  536. {
  537. return $expression . " ESCAPE '\\'";
  538. }
  539. function _sql_custom_build($stage, $data)
  540. {
  541. return $data;
  542. }
  543. function _sql_bit_and($column_name, $bit, $compare = '')
  544. {
  545. return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
  546. }
  547. function _sql_bit_or($column_name, $bit, $compare = '')
  548. {
  549. return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
  550. }
  551. /**
  552. * return sql error array
  553. * @access private
  554. */
  555. function _sql_error()
  556. {
  557. $error = @ocierror();
  558. $error = (!$error) ? @ocierror($this->query_result) : $error;
  559. $error = (!$error) ? @ocierror($this->db_connect_id) : $error;
  560. if ($error)
  561. {
  562. $this->last_error_result = $error;
  563. }
  564. else
  565. {
  566. $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
  567. }
  568. return $error;
  569. }
  570. /**
  571. * Close sql connection
  572. * @access private
  573. */
  574. function _sql_close()
  575. {
  576. return @ocilogoff($this->db_connect_id);
  577. }
  578. /**
  579. * Build db-specific report
  580. * @access private
  581. */
  582. function _sql_report($mode, $query = '')
  583. {
  584. switch ($mode)
  585. {
  586. case 'start':
  587. $html_table = false;
  588. // Grab a plan table, any will do
  589. $sql = "SELECT table_name
  590. FROM USER_TABLES
  591. WHERE table_name LIKE '%PLAN_TABLE%'";
  592. $stmt = ociparse($this->db_connect_id, $sql);
  593. ociexecute($stmt);
  594. $result = array();
  595. if (ocifetchinto($stmt, $result, OCI_ASSOC + OCI_RETURN_NULLS))
  596. {
  597. $table = $result['TABLE_NAME'];
  598. // This is the statement_id that will allow us to track the plan
  599. $statement_id = substr(md5($query), 0, 30);
  600. // Remove any stale plans
  601. $stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
  602. ociexecute($stmt2);
  603. ocifreestatement($stmt2);
  604. // Explain the plan
  605. $sql = "EXPLAIN PLAN
  606. SET STATEMENT_ID = '$statement_id'
  607. FOR $query";
  608. $stmt2 = ociparse($this->db_connect_id, $sql);
  609. ociexecute($stmt2);
  610. ocifreestatement($stmt2);
  611. // Get the data from the plan
  612. $sql = "SELECT operation, options, object_name, object_type, cardinality, cost
  613. FROM plan_table
  614. START WITH id = 0 AND statement_id = '$statement_id'
  615. CONNECT BY PRIOR id = parent_id
  616. AND statement_id = '$statement_id'";
  617. $stmt2 = ociparse($this->db_connect_id, $sql);
  618. ociexecute($stmt2);
  619. $row = array();
  620. while (ocifetchinto($stmt2, $row, OCI_ASSOC + OCI_RETURN_NULLS))
  621. {
  622. $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
  623. }
  624. ocifreestatement($stmt2);
  625. // Remove the plan we just made, we delete them on request anyway
  626. $stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
  627. ociexecute($stmt2);
  628. ocifreestatement($stmt2);
  629. }
  630. ocifreestatement($stmt);
  631. if ($html_table)
  632. {
  633. $this->html_hold .= '</table>';
  634. }
  635. break;
  636. case 'fromcache':
  637. $endtime = explode(' ', microtime());
  638. $endtime = $endtime[0] + $endtime[1];
  639. $result = @ociparse($this->db_connect_id, $query);
  640. $success = @ociexecute($result, OCI_DEFAULT);
  641. $row = array();
  642. while (@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS))
  643. {
  644. // Take the time spent on parsing rows into account
  645. }
  646. @ocifreestatement($result);
  647. $splittime = explode(' ', microtime());
  648. $splittime = $splittime[0] + $splittime[1];
  649. $this->sql_report('record_fromcache', $query, $endtime, $splittime);
  650. break;
  651. }
  652. }
  653. }
  654. ?>