PageRenderTime 62ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/db/dbal.php

https://github.com/sietf/sietf.org
PHP | 865 lines | 590 code | 135 blank | 140 comment | 84 complexity | 768f11b663dbcba9bfb4f8d3383b575a MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * @package dbal
  5. * @version $Id: dbal.php 9178 2008-12-06 11:11:10Z acydburn $
  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. /**
  18. * Database Abstraction Layer
  19. * @package dbal
  20. */
  21. class dbal
  22. {
  23. var $db_connect_id;
  24. var $query_result;
  25. var $return_on_error = false;
  26. var $transaction = false;
  27. var $sql_time = 0;
  28. var $num_queries = array();
  29. var $open_queries = array();
  30. var $curtime = 0;
  31. var $query_hold = '';
  32. var $html_hold = '';
  33. var $sql_report = '';
  34. var $persistency = false;
  35. var $user = '';
  36. var $server = '';
  37. var $dbname = '';
  38. // Set to true if error triggered
  39. var $sql_error_triggered = false;
  40. // Holding the last sql query on sql error
  41. var $sql_error_sql = '';
  42. // Holding the error information - only populated if sql_error_triggered is set
  43. var $sql_error_returned = array();
  44. // Holding transaction count
  45. var $transactions = 0;
  46. // Supports multi inserts?
  47. var $multi_insert = false;
  48. /**
  49. * Current sql layer
  50. */
  51. var $sql_layer = '';
  52. /**
  53. * Wildcards for matching any (%) or exactly one (_) character within LIKE expressions
  54. */
  55. var $any_char;
  56. var $one_char;
  57. /**
  58. * Exact version of the DBAL, directly queried
  59. */
  60. var $sql_server_version = false;
  61. /**
  62. * Constructor
  63. */
  64. function dbal()
  65. {
  66. $this->num_queries = array(
  67. 'cached' => 0,
  68. 'normal' => 0,
  69. 'total' => 0,
  70. );
  71. // Fill default sql layer based on the class being called.
  72. // This can be changed by the specified layer itself later if needed.
  73. $this->sql_layer = substr(get_class($this), 5);
  74. // Do not change this please! This variable is used to easy the use of it - and is hardcoded.
  75. $this->any_char = chr(0) . '%';
  76. $this->one_char = chr(0) . '_';
  77. }
  78. /**
  79. * return on error or display error message
  80. */
  81. function sql_return_on_error($fail = false)
  82. {
  83. $this->sql_error_triggered = false;
  84. $this->sql_error_sql = '';
  85. $this->return_on_error = $fail;
  86. }
  87. /**
  88. * Return number of sql queries and cached sql queries used
  89. */
  90. function sql_num_queries($cached = false)
  91. {
  92. return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal'];
  93. }
  94. /**
  95. * Add to query count
  96. */
  97. function sql_add_num_queries($cached = false)
  98. {
  99. $this->num_queries['cached'] += ($cached !== false) ? 1 : 0;
  100. $this->num_queries['normal'] += ($cached !== false) ? 0 : 1;
  101. $this->num_queries['total'] += 1;
  102. }
  103. /**
  104. * DBAL garbage collection, close sql connection
  105. */
  106. function sql_close()
  107. {
  108. if (!$this->db_connect_id)
  109. {
  110. return false;
  111. }
  112. if ($this->transaction)
  113. {
  114. do
  115. {
  116. $this->sql_transaction('commit');
  117. }
  118. while ($this->transaction);
  119. }
  120. foreach ($this->open_queries as $query_id)
  121. {
  122. $this->sql_freeresult($query_id);
  123. }
  124. // Connection closed correctly. Set db_connect_id to false to prevent errors
  125. if ($result = $this->_sql_close())
  126. {
  127. $this->db_connect_id = false;
  128. }
  129. return $result;
  130. }
  131. /**
  132. * Build LIMIT query
  133. * Doing some validation here.
  134. */
  135. function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
  136. {
  137. if (empty($query))
  138. {
  139. return false;
  140. }
  141. // Never use a negative total or offset
  142. $total = ($total < 0) ? 0 : $total;
  143. $offset = ($offset < 0) ? 0 : $offset;
  144. return $this->_sql_query_limit($query, $total, $offset, $cache_ttl);
  145. }
  146. /**
  147. * Fetch all rows
  148. */
  149. function sql_fetchrowset($query_id = false)
  150. {
  151. if ($query_id === false)
  152. {
  153. $query_id = $this->query_result;
  154. }
  155. if ($query_id !== false)
  156. {
  157. $result = array();
  158. while ($row = $this->sql_fetchrow($query_id))
  159. {
  160. $result[] = $row;
  161. }
  162. return $result;
  163. }
  164. return false;
  165. }
  166. /**
  167. * Fetch field
  168. * if rownum is false, the current row is used, else it is pointing to the row (zero-based)
  169. */
  170. function sql_fetchfield($field, $rownum = false, $query_id = false)
  171. {
  172. global $cache;
  173. if ($query_id === false)
  174. {
  175. $query_id = $this->query_result;
  176. }
  177. if ($query_id !== false)
  178. {
  179. if ($rownum !== false)
  180. {
  181. $this->sql_rowseek($rownum, $query_id);
  182. }
  183. if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
  184. {
  185. return $cache->sql_fetchfield($query_id, $field);
  186. }
  187. $row = $this->sql_fetchrow($query_id);
  188. return (isset($row[$field])) ? $row[$field] : false;
  189. }
  190. return false;
  191. }
  192. /**
  193. * Correctly adjust LIKE expression for special characters
  194. * Some DBMS are handling them in a different way
  195. *
  196. * @param string $expression The expression to use. Every wildcard is escaped, except $this->any_char and $this->one_char
  197. * @return string LIKE expression including the keyword!
  198. */
  199. function sql_like_expression($expression)
  200. {
  201. $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
  202. $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
  203. return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
  204. }
  205. /**
  206. * SQL Transaction
  207. * @access private
  208. */
  209. function sql_transaction($status = 'begin')
  210. {
  211. switch ($status)
  212. {
  213. case 'begin':
  214. // If we are within a transaction we will not open another one, but enclose the current one to not loose data (prevening auto commit)
  215. if ($this->transaction)
  216. {
  217. $this->transactions++;
  218. return true;
  219. }
  220. $result = $this->_sql_transaction('begin');
  221. if (!$result)
  222. {
  223. $this->sql_error();
  224. }
  225. $this->transaction = true;
  226. break;
  227. case 'commit':
  228. // If there was a previously opened transaction we do not commit yet... but count back the number of inner transactions
  229. if ($this->transaction && $this->transactions)
  230. {
  231. $this->transactions--;
  232. return true;
  233. }
  234. // Check if there is a transaction (no transaction can happen if there was an error, with a combined rollback and error returning enabled)
  235. // This implies we have transaction always set for autocommit db's
  236. if (!$this->transaction)
  237. {
  238. return false;
  239. }
  240. $result = $this->_sql_transaction('commit');
  241. if (!$result)
  242. {
  243. $this->sql_error();
  244. }
  245. $this->transaction = false;
  246. $this->transactions = 0;
  247. break;
  248. case 'rollback':
  249. $result = $this->_sql_transaction('rollback');
  250. $this->transaction = false;
  251. $this->transactions = 0;
  252. break;
  253. default:
  254. $result = $this->_sql_transaction($status);
  255. break;
  256. }
  257. return $result;
  258. }
  259. /**
  260. * Build sql statement from array for insert/update/select statements
  261. *
  262. * Idea for this from Ikonboard
  263. * Possible query values: INSERT, INSERT_SELECT, UPDATE, SELECT
  264. *
  265. */
  266. function sql_build_array($query, $assoc_ary = false)
  267. {
  268. if (!is_array($assoc_ary))
  269. {
  270. return false;
  271. }
  272. $fields = $values = array();
  273. if ($query == 'INSERT' || $query == 'INSERT_SELECT')
  274. {
  275. foreach ($assoc_ary as $key => $var)
  276. {
  277. $fields[] = $key;
  278. if (is_array($var) && is_string($var[0]))
  279. {
  280. // This is used for INSERT_SELECT(s)
  281. $values[] = $var[0];
  282. }
  283. else
  284. {
  285. $values[] = $this->_sql_validate_value($var);
  286. }
  287. }
  288. $query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' ';
  289. }
  290. else if ($query == 'MULTI_INSERT')
  291. {
  292. trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
  293. }
  294. else if ($query == 'UPDATE' || $query == 'SELECT')
  295. {
  296. $values = array();
  297. foreach ($assoc_ary as $key => $var)
  298. {
  299. $values[] = "$key = " . $this->_sql_validate_value($var);
  300. }
  301. $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
  302. }
  303. return $query;
  304. }
  305. /**
  306. * Build IN or NOT IN sql comparison string, uses <> or = on single element
  307. * arrays to improve comparison speed
  308. *
  309. * @access public
  310. * @param string $field name of the sql column that shall be compared
  311. * @param array $array array of values that are allowed (IN) or not allowed (NOT IN)
  312. * @param bool $negate true for NOT IN (), false for IN () (default)
  313. * @param bool $allow_empty_set If true, allow $array to be empty, this function will return 1=1 or 1=0 then. Default to false.
  314. */
  315. function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
  316. {
  317. if (!sizeof($array))
  318. {
  319. if (!$allow_empty_set)
  320. {
  321. // Print the backtrace to help identifying the location of the problematic code
  322. $this->sql_error('No values specified for SQL IN comparison');
  323. }
  324. else
  325. {
  326. // NOT IN () actually means everything so use a tautology
  327. if ($negate)
  328. {
  329. return '1=1';
  330. }
  331. // IN () actually means nothing so use a contradiction
  332. else
  333. {
  334. return '1=0';
  335. }
  336. }
  337. }
  338. if (!is_array($array))
  339. {
  340. $array = array($array);
  341. }
  342. if (sizeof($array) == 1)
  343. {
  344. @reset($array);
  345. $var = current($array);
  346. return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var);
  347. }
  348. else
  349. {
  350. return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')';
  351. }
  352. }
  353. /**
  354. * Run more than one insert statement.
  355. *
  356. * @param string $table table name to run the statements on
  357. * @param array &$sql_ary multi-dimensional array holding the statement data.
  358. *
  359. * @return bool false if no statements were executed.
  360. * @access public
  361. */
  362. function sql_multi_insert($table, &$sql_ary)
  363. {
  364. if (!sizeof($sql_ary))
  365. {
  366. return false;
  367. }
  368. if ($this->multi_insert)
  369. {
  370. $ary = array();
  371. foreach ($sql_ary as $id => $_sql_ary)
  372. {
  373. // If by accident the sql array is only one-dimensional we build a normal insert statement
  374. if (!is_array($_sql_ary))
  375. {
  376. $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
  377. return true;
  378. }
  379. $values = array();
  380. foreach ($_sql_ary as $key => $var)
  381. {
  382. $values[] = $this->_sql_validate_value($var);
  383. }
  384. $ary[] = '(' . implode(', ', $values) . ')';
  385. }
  386. $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
  387. }
  388. else
  389. {
  390. foreach ($sql_ary as $ary)
  391. {
  392. if (!is_array($ary))
  393. {
  394. return false;
  395. }
  396. $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
  397. }
  398. }
  399. return true;
  400. }
  401. /**
  402. * Function for validating values
  403. * @access private
  404. */
  405. function _sql_validate_value($var)
  406. {
  407. if (is_null($var))
  408. {
  409. return 'NULL';
  410. }
  411. else if (is_string($var))
  412. {
  413. return "'" . $this->sql_escape($var) . "'";
  414. }
  415. else
  416. {
  417. return (is_bool($var)) ? intval($var) : $var;
  418. }
  419. }
  420. /**
  421. * Build sql statement from array for select and select distinct statements
  422. *
  423. * Possible query values: SELECT, SELECT_DISTINCT
  424. */
  425. function sql_build_query($query, $array)
  426. {
  427. $sql = '';
  428. switch ($query)
  429. {
  430. case 'SELECT':
  431. case 'SELECT_DISTINCT';
  432. $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
  433. // Build table array. We also build an alias array for later checks.
  434. $table_array = $aliases = array();
  435. $used_multi_alias = false;
  436. foreach ($array['FROM'] as $table_name => $alias)
  437. {
  438. if (is_array($alias))
  439. {
  440. $used_multi_alias = true;
  441. foreach ($alias as $multi_alias)
  442. {
  443. $table_array[] = $table_name . ' ' . $multi_alias;
  444. $aliases[] = $multi_alias;
  445. }
  446. }
  447. else
  448. {
  449. $table_array[] = $table_name . ' ' . $alias;
  450. $aliases[] = $alias;
  451. }
  452. }
  453. // We run the following code to determine if we need to re-order the table array. ;)
  454. // The reason for this is that for multi-aliased tables (two equal tables) in the FROM statement the last table need to match the first comparison.
  455. // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
  456. if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false)
  457. {
  458. // Take first LEFT JOIN
  459. $join = current($array['LEFT_JOIN']);
  460. // Determine the table used there (even if there are more than one used, we only want to have one
  461. preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
  462. // If there is a first join match, we need to make sure the table order is correct
  463. if (!empty($matches[1]))
  464. {
  465. $first_join_match = trim($matches[1]);
  466. $table_array = $last = array();
  467. foreach ($array['FROM'] as $table_name => $alias)
  468. {
  469. if (is_array($alias))
  470. {
  471. foreach ($alias as $multi_alias)
  472. {
  473. ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
  474. }
  475. }
  476. else
  477. {
  478. ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
  479. }
  480. }
  481. $table_array = array_merge($table_array, $last);
  482. }
  483. }
  484. $sql .= $this->_sql_custom_build('FROM', implode(', ', $table_array));
  485. if (!empty($array['LEFT_JOIN']))
  486. {
  487. foreach ($array['LEFT_JOIN'] as $join)
  488. {
  489. $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
  490. }
  491. }
  492. if (!empty($array['WHERE']))
  493. {
  494. $sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']);
  495. }
  496. if (!empty($array['GROUP_BY']))
  497. {
  498. $sql .= ' GROUP BY ' . $array['GROUP_BY'];
  499. }
  500. if (!empty($array['ORDER_BY']))
  501. {
  502. $sql .= ' ORDER BY ' . $array['ORDER_BY'];
  503. }
  504. break;
  505. }
  506. return $sql;
  507. }
  508. /**
  509. * display sql error page
  510. */
  511. function sql_error($sql = '')
  512. {
  513. global $auth, $user, $config;
  514. // Set var to retrieve errored status
  515. $this->sql_error_triggered = true;
  516. $this->sql_error_sql = $sql;
  517. $this->sql_error_returned = $this->_sql_error();
  518. if (!$this->return_on_error)
  519. {
  520. $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
  521. // Show complete SQL error and path to administrators only
  522. // Additionally show complete error on installation or if extended debug mode is enabled
  523. // The DEBUG_EXTRA constant is for development only!
  524. if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || defined('DEBUG_EXTRA'))
  525. {
  526. // Print out a nice backtrace...
  527. $backtrace = get_backtrace();
  528. $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : '';
  529. $message .= ($backtrace) ? '<br /><br />BACKTRACE<br />' . $backtrace : '';
  530. $message .= '<br />';
  531. }
  532. else
  533. {
  534. // If error occurs in initiating the session we need to use a pre-defined language string
  535. // This could happen if the connection could not be established for example (then we are not able to grab the default language)
  536. if (!isset($user->lang['SQL_ERROR_OCCURRED']))
  537. {
  538. $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
  539. }
  540. else
  541. {
  542. if (!empty($config['board_contact']))
  543. {
  544. $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
  545. }
  546. else
  547. {
  548. $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
  549. }
  550. }
  551. }
  552. if ($this->transaction)
  553. {
  554. $this->sql_transaction('rollback');
  555. }
  556. if (strlen($message) > 1024)
  557. {
  558. // We need to define $msg_long_text here to circumvent text stripping.
  559. global $msg_long_text;
  560. $msg_long_text = $message;
  561. trigger_error(false, E_USER_ERROR);
  562. }
  563. trigger_error($message, E_USER_ERROR);
  564. }
  565. if ($this->transaction)
  566. {
  567. $this->sql_transaction('rollback');
  568. }
  569. return $this->sql_error_returned;
  570. }
  571. /**
  572. * Explain queries
  573. */
  574. function sql_report($mode, $query = '')
  575. {
  576. global $cache, $starttime, $phpbb_root_path, $user;
  577. if (empty($_REQUEST['explain']))
  578. {
  579. return false;
  580. }
  581. if (!$query && $this->query_hold != '')
  582. {
  583. $query = $this->query_hold;
  584. }
  585. switch ($mode)
  586. {
  587. case 'display':
  588. if (!empty($cache))
  589. {
  590. $cache->unload();
  591. }
  592. $this->sql_close();
  593. $mtime = explode(' ', microtime());
  594. $totaltime = $mtime[0] + $mtime[1] - $starttime;
  595. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  596. <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
  597. <head>
  598. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  599. <meta http-equiv="Content-Style-Type" content="text/css" />
  600. <meta http-equiv="imagetoolbar" content="no" />
  601. <title>SQL Report</title>
  602. <link href="' . $phpbb_root_path . 'adm/style/admin.css" rel="stylesheet" type="text/css" media="screen" />
  603. </head>
  604. <body id="errorpage">
  605. <div id="wrap">
  606. <div id="page-header">
  607. <a href="' . build_url('explain') . '">Return to previous page</a>
  608. </div>
  609. <div id="page-body">
  610. <div id="acp">
  611. <div class="panel">
  612. <span class="corners-top"><span></span></span>
  613. <div id="content">
  614. <h1>SQL Report</h1>
  615. <br />
  616. <p><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries['normal']} queries" . (($this->num_queries['cached']) ? " + {$this->num_queries['cached']} " . (($this->num_queries['cached'] == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></p>
  617. <p>Time spent on ' . $this->sql_layer . ' queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></p>
  618. <br /><br />
  619. ' . $this->sql_report . '
  620. </div>
  621. <span class="corners-bottom"><span></span></span>
  622. </div>
  623. </div>
  624. </div>
  625. <div id="page-footer">
  626. Powered by phpBB &copy; 2000, 2002, 2005, 2007 <a href="http://www.phpbb.com/">phpBB Group</a>
  627. </div>
  628. </div>
  629. </body>
  630. </html>';
  631. exit_handler();
  632. break;
  633. case 'stop':
  634. $endtime = explode(' ', microtime());
  635. $endtime = $endtime[0] + $endtime[1];
  636. $this->sql_report .= '
  637. <table cellspacing="1">
  638. <thead>
  639. <tr>
  640. <th>Query #' . $this->num_queries['total'] . '</th>
  641. </tr>
  642. </thead>
  643. <tbody>
  644. <tr>
  645. <td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td>
  646. </tr>
  647. </tbody>
  648. </table>
  649. ' . $this->html_hold . '
  650. <p style="text-align: center;">
  651. ';
  652. if ($this->query_result)
  653. {
  654. if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
  655. {
  656. $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows($this->query_result) . '</b> | ';
  657. }
  658. $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $this->curtime) . 's</b>';
  659. }
  660. else
  661. {
  662. $error = $this->sql_error();
  663. $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
  664. }
  665. $this->sql_report .= '</p><br /><br />';
  666. $this->sql_time += $endtime - $this->curtime;
  667. break;
  668. case 'start':
  669. $this->query_hold = $query;
  670. $this->html_hold = '';
  671. $this->_sql_report($mode, $query);
  672. $this->curtime = explode(' ', microtime());
  673. $this->curtime = $this->curtime[0] + $this->curtime[1];
  674. break;
  675. case 'add_select_row':
  676. $html_table = func_get_arg(2);
  677. $row = func_get_arg(3);
  678. if (!$html_table && sizeof($row))
  679. {
  680. $html_table = true;
  681. $this->html_hold .= '<table cellspacing="1"><tr>';
  682. foreach (array_keys($row) as $val)
  683. {
  684. $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
  685. }
  686. $this->html_hold .= '</tr>';
  687. }
  688. $this->html_hold .= '<tr>';
  689. $class = 'row1';
  690. foreach (array_values($row) as $val)
  691. {
  692. $class = ($class == 'row1') ? 'row2' : 'row1';
  693. $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
  694. }
  695. $this->html_hold .= '</tr>';
  696. return $html_table;
  697. break;
  698. case 'fromcache':
  699. $this->_sql_report($mode, $query);
  700. break;
  701. case 'record_fromcache':
  702. $endtime = func_get_arg(2);
  703. $splittime = func_get_arg(3);
  704. $time_cache = $endtime - $this->curtime;
  705. $time_db = $splittime - $endtime;
  706. $color = ($time_db > $time_cache) ? 'green' : 'red';
  707. $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
  708. $this->sql_report .= '<td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></tbody></table>';
  709. $this->sql_report .= '<p style="text-align: center;">';
  710. $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p><br /><br />';
  711. // Pad the start time to not interfere with page timing
  712. $starttime += $time_db;
  713. break;
  714. default:
  715. $this->_sql_report($mode, $query);
  716. break;
  717. }
  718. return true;
  719. }
  720. }
  721. /**
  722. * This variable holds the class name to use later
  723. */
  724. $sql_db = (!empty($dbms)) ? 'dbal_' . basename($dbms) : 'dbal';
  725. ?>