PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/phpBB/includes/db/oracle.php

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