PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/upload/includes/db/mysql.php

http://torrentpier2.googlecode.com/
PHP | 987 lines | 732 code | 120 blank | 135 comment | 113 complexity | 513a43f3126e4b94d79e28260cca72ec MD5 | raw file
  1. <?php
  2. if (!defined('SQL_DEBUG')) die(basename(__FILE__));
  3. class sql_db
  4. {
  5. var $cfg = array();
  6. var $cfg_keys = array('dbhost', 'dbname', 'dbuser', 'dbpasswd', 'charset', 'persist');
  7. var $link = null;
  8. var $result = null;
  9. var $db_server = '';
  10. var $selected_db = null;
  11. var $inited = false;
  12. var $locked = false;
  13. var $locks = array();
  14. var $num_queries = 0;
  15. var $sql_starttime = 0;
  16. var $sql_inittime = 0;
  17. var $sql_timetotal = 0;
  18. var $cur_query_time = 0;
  19. var $slow_time = 0;
  20. var $dbg = array();
  21. var $dbg_id = 0;
  22. var $dbg_enabled = false;
  23. var $cur_query = null;
  24. var $do_explain = false;
  25. var $explain_hold = '';
  26. var $explain_out = '';
  27. var $shutdown = array();
  28. var $DBS = array();
  29. /**
  30. * Constructor
  31. */
  32. function sql_db ($cfg_values)
  33. {
  34. global $DBS;
  35. $this->cfg = array_combine($this->cfg_keys, $cfg_values);
  36. $this->dbg_enabled = (sql_dbg_enabled() || !empty($_COOKIE['explain']));
  37. $this->do_explain = ($this->dbg_enabled && !empty($_COOKIE['explain']));
  38. $this->slow_time = SQL_SLOW_QUERY_TIME;
  39. // ?????? ?? ?????????? ?????????? (??? ????????? ????? ????? ?? ???? ????????, ???????? ?????? ?????????? ???????? ? ?.?.)
  40. $this->DBS['log_file'] =& $DBS->log_file;
  41. $this->DBS['log_counter'] =& $DBS->log_counter;
  42. $this->DBS['num_queries'] =& $DBS->num_queries;
  43. $this->DBS['sql_inittime'] =& $DBS->sql_inittime;
  44. $this->DBS['sql_timetotal'] =& $DBS->sql_timetotal;
  45. }
  46. /**
  47. * Initialize connection
  48. */
  49. function init ()
  50. {
  51. // Connect to server
  52. $this->link = $this->connect();
  53. // Select database
  54. $this->selected_db = $this->select_db();
  55. // Set charset
  56. if ($this->cfg['charset'] && !@mysql_set_charset($this->cfg['charset'], $this->link))
  57. {
  58. if (!$this->sql_query("SET NAMES {$this->cfg['charset']}"))
  59. {
  60. die("Could not set charset {$this->cfg['charset']}");
  61. }
  62. }
  63. $this->inited = true;
  64. $this->num_queries = 0;
  65. $this->sql_inittime = $this->sql_timetotal;
  66. $this->DBS['sql_inittime'] += $this->sql_inittime;
  67. }
  68. /**
  69. * Open connection
  70. */
  71. function connect ()
  72. {
  73. $this->cur_query = ($this->dbg_enabled) ? ($this->cfg['persist'] ? 'p' : '') . "connect to: {$this->cfg['dbhost']}" : 'connect';
  74. $this->debug('start');
  75. $connect_type = ($this->cfg['persist']) ? 'mysql_pconnect' : 'mysql_connect';
  76. if (!$link = @$connect_type($this->cfg['dbhost'], $this->cfg['dbuser'], $this->cfg['dbpasswd']))
  77. {
  78. $server = (DBG_USER) ? $this->cfg['dbhost'] : '';
  79. header("HTTP/1.0 503 Service Unavailable");
  80. bb_log(' ', "db_err/connect_failed_{$this->cfg['dbhost']}");
  81. die("Could not connect to the server $server");
  82. }
  83. register_shutdown_function(array(&$this, 'close'));
  84. $this->debug('stop');
  85. $this->cur_query = null;
  86. return $link;
  87. }
  88. /**
  89. * Select database
  90. */
  91. function select_db ()
  92. {
  93. $this->cur_query = ($this->dbg_enabled) ? "select db: {$this->cfg['dbname']}" : 'select db';
  94. $this->debug('start');
  95. if (!@mysql_select_db($this->cfg['dbname'], $this->link))
  96. {
  97. $database = (DBG_USER) ? $this->cfg['dbhost'] : '';
  98. die("Could not select database $database");
  99. }
  100. $this->debug('stop');
  101. $this->cur_query = null;
  102. return $this->cfg['dbname'];
  103. }
  104. /**
  105. * Base query method
  106. */
  107. function sql_query ($query)
  108. {
  109. if (!is_resource($this->link))
  110. {
  111. $this->init();
  112. }
  113. if (is_array($query))
  114. {
  115. $query = $this->build_sql($query);
  116. }
  117. if (SQL_PREPEND_SRC_COMM)
  118. {
  119. $query = '/* '. $this->debug_find_source() .' */ '. $query;
  120. }
  121. $this->cur_query = $query;
  122. $this->debug('start');
  123. if (!$this->result = mysql_query($query, $this->link))
  124. {
  125. $this->log_error();
  126. }
  127. $this->debug('stop');
  128. $this->cur_query = null;
  129. if ($this->inited)
  130. {
  131. $this->num_queries++;
  132. $this->DBS['num_queries']++;
  133. }
  134. return $this->result;
  135. }
  136. /**
  137. * Execute query WRAPPER (with error handling)
  138. */
  139. function query ($query)
  140. {
  141. if (!$result = $this->sql_query($query))
  142. {
  143. $this->trigger_error();
  144. }
  145. return $result;
  146. }
  147. /**
  148. * Return number of rows
  149. */
  150. function num_rows ($result = false)
  151. {
  152. $num_rows = false;
  153. if ($result OR $result = $this->result)
  154. {
  155. $num_rows = is_resource($result) ? mysql_num_rows($result) : false;
  156. }
  157. return $num_rows;
  158. }
  159. /**
  160. * Return number of affected rows
  161. */
  162. function affected_rows ()
  163. {
  164. return is_resource($this->link) ? mysql_affected_rows($this->link) : -1;
  165. }
  166. /**
  167. * Fetch current field
  168. */
  169. function sql_fetchfield($field, $rownum = -1, $query_id = 0)
  170. {
  171. if(!$query_id)
  172. {
  173. $query_id = $this->query_result;
  174. }
  175. if($query_id)
  176. {
  177. if($rownum > -1)
  178. {
  179. $result = @mysql_result($query_id, $rownum, $field);
  180. }
  181. else
  182. {
  183. if(empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
  184. {
  185. if($this->sql_fetchrow())
  186. {
  187. $result = $this->row[$query_id][$field];
  188. }
  189. }
  190. else
  191. {
  192. if($this->rowset[$query_id])
  193. {
  194. $result = $this->rowset[$query_id][0][$field];
  195. }
  196. else if($this->row[$query_id])
  197. {
  198. $result = $this->row[$query_id][$field];
  199. }
  200. }
  201. }
  202. return $result;
  203. }
  204. else
  205. {
  206. return false;
  207. }
  208. }
  209. /**
  210. * Fetch current row
  211. */
  212. function sql_fetchrow ($result, $field_name = '')
  213. {
  214. $row = mysql_fetch_assoc($result);
  215. if ($field_name)
  216. {
  217. return isset($row[$field_name]) ? $row[$field_name] : false;
  218. }
  219. else
  220. {
  221. return $row;
  222. }
  223. }
  224. /**
  225. * Alias of sql_fetchrow()
  226. */
  227. function fetch_next ($result)
  228. {
  229. return $this->sql_fetchrow($result);
  230. }
  231. /**
  232. * Fetch row WRAPPER (with error handling)
  233. */
  234. function fetch_row ($query, $field_name = '')
  235. {
  236. if (!$result = $this->sql_query($query))
  237. {
  238. $this->trigger_error();
  239. }
  240. return $this->sql_fetchrow($result, $field_name);
  241. }
  242. /**
  243. * Fetch all rows
  244. */
  245. function sql_fetchrowset ($result, $field_name = '')
  246. {
  247. $rowset = array();
  248. while ($row = mysql_fetch_assoc($result))
  249. {
  250. $rowset[] = ($field_name) ? $row[$field_name] : $row;
  251. }
  252. return $rowset;
  253. }
  254. /**
  255. * Fetch all rows WRAPPER (with error handling)
  256. */
  257. function fetch_rowset ($query, $field_name = '')
  258. {
  259. if (!$result = $this->sql_query($query))
  260. {
  261. $this->trigger_error();
  262. }
  263. return $this->sql_fetchrowset($result, $field_name);
  264. }
  265. /**
  266. * Fetch all rows WRAPPER (with error handling)
  267. */
  268. function fetch_all ($query, $field_name = '')
  269. {
  270. if (!$result = $this->sql_query($query))
  271. {
  272. $this->trigger_error();
  273. }
  274. return $this->sql_fetchrowset($result, $field_name);
  275. }
  276. /**
  277. * Get last inserted id after insert statement
  278. */
  279. function sql_nextid ()
  280. {
  281. return mysql_insert_id($this->link);
  282. }
  283. /**
  284. * Free sql result
  285. */
  286. function sql_freeresult ($result = false)
  287. {
  288. if ($result OR $result = $this->result)
  289. {
  290. $return_value = is_resource($result) ? mysql_free_result($result) : false;
  291. }
  292. $this->result = null;
  293. }
  294. /**
  295. * Escape data used in sql query
  296. */
  297. function escape ($v, $check_type = false, $dont_escape = false)
  298. {
  299. if ($dont_escape) return $v;
  300. if (!$check_type) return $this->escape_string($v);
  301. switch (true)
  302. {
  303. case is_string ($v): return "'". $this->escape_string($v) ."'";
  304. case is_int ($v): return "$v";
  305. case is_bool ($v): return ($v) ? '1' : '0';
  306. case is_float ($v): return "'$v'";
  307. case is_null ($v): return 'NULL';
  308. }
  309. // if $v has unsuitable type
  310. $this->trigger_error(__FUNCTION__ .' - wrong params');
  311. }
  312. /**
  313. * Escape string
  314. */
  315. function escape_string ($str)
  316. {
  317. if (!is_resource($this->link))
  318. {
  319. $this->init();
  320. }
  321. return mysql_real_escape_string($str, $this->link);
  322. }
  323. /**
  324. * Build SQL statement from array (based on same method from phpBB3, idea from Ikonboard)
  325. *
  326. * Possible $query_type values: INSERT, INSERT_SELECT, MULTI_INSERT, UPDATE, SELECT
  327. */
  328. function build_array ($query_type, $input_ary, $data_already_escaped = false, $check_data_type_in_escape = true)
  329. {
  330. $fields = $values = $ary = $query = array();
  331. $dont_escape = $data_already_escaped;
  332. $check_type = $check_data_type_in_escape;
  333. if (empty($input_ary) || !is_array($input_ary))
  334. {
  335. $this->trigger_error(__FUNCTION__ .' - wrong params: $input_ary');
  336. }
  337. if ($query_type == 'INSERT')
  338. {
  339. foreach ($input_ary as $field => $val)
  340. {
  341. $fields[] = $field;
  342. $values[] = $this->escape($val, $check_type, $dont_escape);
  343. }
  344. $fields = join(', ', $fields);
  345. $values = join(', ', $values);
  346. $query = "($fields)\nVALUES\n($values)";
  347. }
  348. else if ($query_type == 'INSERT_SELECT')
  349. {
  350. foreach ($input_ary as $field => $val)
  351. {
  352. $fields[] = $field;
  353. $values[] = $this->escape($val, $check_type, $dont_escape);
  354. }
  355. $fields = join(', ', $fields);
  356. $values = join(', ', $values);
  357. $query = "($fields)\nSELECT\n$values";
  358. }
  359. else if ($query_type == 'MULTI_INSERT')
  360. {
  361. foreach ($input_ary as $id => $sql_ary)
  362. {
  363. foreach ($sql_ary as $field => $val)
  364. {
  365. $values[] = $this->escape($val, $check_type, $dont_escape);
  366. }
  367. $ary[] = '('. join(', ', $values) .')';
  368. $values = array();
  369. }
  370. $fields = join(', ', array_keys($input_ary[0]));
  371. $values = join(",\n", $ary);
  372. $query = "($fields)\nVALUES\n$values";
  373. }
  374. else if ($query_type == 'SELECT' || $query_type == 'UPDATE')
  375. {
  376. foreach ($input_ary as $field => $val)
  377. {
  378. $ary[] = "$field = ". $this->escape($val, $check_type, $dont_escape);
  379. }
  380. $glue = ($query_type == 'SELECT') ? "\nAND " : ",\n";
  381. $query = join($glue, $ary);
  382. }
  383. if (!$query)
  384. {
  385. bb_die('<pre><b>'. __FUNCTION__ ."</b>: Wrong params for <b>$query_type</b> query type\n\n\$input_ary:\n\n". htmlCHR(print_r($input_ary, true)) .'</pre>');
  386. }
  387. return "\n". $query ."\n";
  388. }
  389. function get_empty_sql_array ()
  390. {
  391. return array(
  392. 'SELECT' => array(),
  393. 'select_options' => array(),
  394. 'FROM' => array(),
  395. 'INNER JOIN' => array(),
  396. 'LEFT JOIN' => array(),
  397. 'WHERE' => array(),
  398. 'GROUP BY' => array(),
  399. 'HAVING' => array(),
  400. 'ORDER BY' => array(),
  401. 'LIMIT' => array(),
  402. );
  403. }
  404. function build_sql ($sql_ary)
  405. {
  406. $sql = '';
  407. array_deep($sql_ary, 'array_unique', false, true);
  408. foreach ($sql_ary as $clause => $ary)
  409. {
  410. switch ($clause)
  411. {
  412. case 'SELECT':
  413. $sql .= ($ary) ? ' SELECT '. join(' ', $sql_ary['select_options']) .' '. join(', ', $ary) : '';
  414. break;
  415. case 'FROM':
  416. $sql .= ($ary) ? ' FROM '. join(', ', $ary) : '';
  417. break;
  418. case 'INNER JOIN':
  419. $sql .= ($ary) ? ' INNER JOIN '. join(' INNER JOIN ', $ary) : '';
  420. break;
  421. case 'LEFT JOIN':
  422. $sql .= ($ary) ? ' LEFT JOIN '. join(' LEFT JOIN ', $ary) : '';
  423. break;
  424. case 'WHERE':
  425. $sql .= ($ary) ? ' WHERE '. join(' AND ', $ary) : '';
  426. break;
  427. case 'GROUP BY':
  428. $sql .= ($ary) ? ' GROUP BY '. join(', ', $ary) : '';
  429. break;
  430. case 'HAVING':
  431. $sql .= ($ary) ? ' HAVING '. join(' AND ', $ary) : '';
  432. break;
  433. case 'ORDER BY':
  434. $sql .= ($ary) ? ' ORDER BY '. join(', ', $ary) : '';
  435. break;
  436. case 'LIMIT':
  437. $sql .= ($ary) ? ' LIMIT '. join(', ', $ary) : '';
  438. break;
  439. }
  440. }
  441. return trim($sql);
  442. }
  443. /**
  444. * Return sql error array
  445. */
  446. function sql_error ()
  447. {
  448. if (is_resource($this->link))
  449. {
  450. return array('code' => mysql_errno($this->link), 'message' => mysql_error($this->link));
  451. }
  452. else
  453. {
  454. return array('code' => '', 'message' => 'not connected');
  455. }
  456. }
  457. /**
  458. * Close sql connection
  459. */
  460. function close ()
  461. {
  462. if (is_resource($this->link))
  463. {
  464. $this->unlock();
  465. if (!empty($this->locks))
  466. {
  467. foreach ($this->locks as $name => $void)
  468. {
  469. $this->release_lock($name);
  470. }
  471. }
  472. $this->exec_shutdown_queries();
  473. mysql_close($this->link);
  474. }
  475. $this->link = $this->selected_db = null;
  476. }
  477. /**
  478. * Add shutdown query
  479. */
  480. function add_shutdown_query ($sql)
  481. {
  482. $this->shutdown['__sql'][] = $sql;
  483. }
  484. /**
  485. * Exec shutdown queries
  486. */
  487. function exec_shutdown_queries ()
  488. {
  489. if (empty($this->shutdown)) return;
  490. if (!empty($this->shutdown['post_html']))
  491. {
  492. $post_html_sql = $this->build_array('MULTI_INSERT', $this->shutdown['post_html']);
  493. $this->query("REPLACE INTO ". BB_POSTS_HTML ." $post_html_sql");
  494. }
  495. if (!empty($this->shutdown['__sql']))
  496. {
  497. foreach ($this->shutdown['__sql'] as $sql)
  498. {
  499. $this->query($sql);
  500. }
  501. }
  502. }
  503. /**
  504. * Lock tables
  505. */
  506. function lock ($tables, $lock_type = 'WRITE')
  507. {
  508. if ($this->cfg['persist'])
  509. {
  510. # return true;
  511. }
  512. $tables_sql = array();
  513. foreach ((array) $tables as $table_name)
  514. {
  515. $tables_sql[] = "$table_name $lock_type";
  516. }
  517. if ($tables_sql = join(', ', $tables_sql))
  518. {
  519. $this->locked = $this->sql_query("LOCK TABLES $tables_sql");
  520. }
  521. return $this->locked;
  522. }
  523. /**
  524. * Unlock tables
  525. */
  526. function unlock ()
  527. {
  528. if ($this->locked && $this->sql_query("UNLOCK TABLES"))
  529. {
  530. $this->locked = false;
  531. }
  532. return !$this->locked;
  533. }
  534. /**
  535. * Obtain user level lock
  536. */
  537. function get_lock ($name, $timeout = 0)
  538. {
  539. $lock_name = $this->get_lock_name($name);
  540. $timeout = (int) $timeout;
  541. $row = $this->fetch_row("SELECT GET_LOCK('$lock_name', $timeout) AS lock_result");
  542. if ($row['lock_result'])
  543. {
  544. $this->locks[$name] = true;
  545. }
  546. return $row['lock_result'];
  547. }
  548. /**
  549. * Obtain user level lock status
  550. */
  551. function release_lock ($name)
  552. {
  553. $lock_name = $this->get_lock_name($name);
  554. $row = $this->fetch_row("SELECT RELEASE_LOCK('$lock_name') AS lock_result");
  555. if ($row['lock_result'])
  556. {
  557. unset($this->locks[$name]);
  558. }
  559. return $row['lock_result'];
  560. }
  561. /**
  562. * Release user level lock
  563. */
  564. function is_free_lock ($name)
  565. {
  566. $lock_name = $this->get_lock_name($name);
  567. $row = $this->fetch_row("SELECT IS_FREE_LOCK('$lock_name') AS lock_result");
  568. return $row['lock_result'];
  569. }
  570. /**
  571. * Make per db unique lock name
  572. */
  573. function get_lock_name ($name)
  574. {
  575. if (!$this->selected_db)
  576. {
  577. $this->init();
  578. }
  579. return "{$this->selected_db}_{$name}";
  580. }
  581. /**
  582. * Get info about last query
  583. */
  584. function query_info ()
  585. {
  586. $info = array();
  587. if ($num = $this->num_rows($this->result))
  588. {
  589. $info[] = "$num rows";
  590. }
  591. if (is_resource($this->link) AND $ext = mysql_info($this->link))
  592. {
  593. $info[] = "$ext";
  594. }
  595. else if (!$num && ($aff = $this->affected_rows($this->result) AND $aff != -1))
  596. {
  597. $info[] = "$aff rows";
  598. }
  599. return str_compact(join(', ', $info));
  600. }
  601. /**
  602. * Get server version
  603. */
  604. function server_version ()
  605. {
  606. preg_match('#^(\d+\.\d+\.\d+).*#', mysql_get_server_info(), $m);
  607. return $m[1];
  608. }
  609. /**
  610. * Set slow query marker for xx seconds
  611. * This will disable counting other queries as "slow" during this time
  612. */
  613. function expect_slow_query ($ignoring_time = 60, $new_priority = 10)
  614. {
  615. if ($old_priority = CACHE('bb_cache')->get('dont_log_slow_query'))
  616. {
  617. if ($old_priority > $new_priority)
  618. {
  619. return;
  620. }
  621. }
  622. @define('IN_FIRST_SLOW_QUERY', true);
  623. CACHE('bb_cache')->set('dont_log_slow_query', $new_priority, $ignoring_time);
  624. }
  625. /**
  626. * Store debug info
  627. */
  628. function debug ($mode)
  629. {
  630. if (!SQL_DEBUG) return;
  631. $id =& $this->dbg_id;
  632. $dbg =& $this->dbg[$id];
  633. if ($mode == 'start')
  634. {
  635. if (SQL_CALC_QUERY_TIME || DBG_LOG || SQL_LOG_SLOW_QUERIES)
  636. {
  637. $this->sql_starttime = utime();
  638. }
  639. if ($this->dbg_enabled)
  640. {
  641. $dbg['sql'] = preg_replace('#^(\s*)(/\*)(.*)(\*/)(\s*)#', '', $this->cur_query);
  642. $dbg['src'] = $this->debug_find_source();
  643. $dbg['file'] = $this->debug_find_source('file');
  644. $dbg['line'] = $this->debug_find_source('line');
  645. $dbg['time'] = '';
  646. $dbg['info'] = '';
  647. $dbg['mem_before'] = sys('mem');
  648. }
  649. if ($this->do_explain)
  650. {
  651. $this->explain('start');
  652. }
  653. }
  654. else if ($mode == 'stop')
  655. {
  656. if (SQL_CALC_QUERY_TIME || DBG_LOG || SQL_LOG_SLOW_QUERIES)
  657. {
  658. $this->cur_query_time = utime() - $this->sql_starttime;
  659. $this->sql_timetotal += $this->cur_query_time;
  660. $this->DBS['sql_timetotal'] += $this->cur_query_time;
  661. if (SQL_LOG_SLOW_QUERIES && $this->cur_query_time > $this->slow_time)
  662. {
  663. $this->log_slow_query();
  664. }
  665. }
  666. if ($this->dbg_enabled)
  667. {
  668. $dbg['time'] = utime() - $this->sql_starttime;
  669. $dbg['info'] = $this->query_info();
  670. $dbg['mem_after'] = sys('mem');
  671. $id++;
  672. }
  673. if ($this->do_explain)
  674. {
  675. $this->explain('stop');
  676. }
  677. // ???????? ????????? $this->inited - ??? ???????? ????????????????? ????????
  678. if ($this->DBS['log_counter'] && $this->inited)
  679. {
  680. $this->log_query($this->DBS['log_file']);
  681. $this->DBS['log_counter']--;
  682. }
  683. }
  684. }
  685. /**
  686. * Trigger error
  687. */
  688. function trigger_error ($msg = 'DB Error')
  689. {
  690. if (error_reporting())
  691. {
  692. if (DEBUG === true)
  693. {
  694. $err = $this->sql_error();
  695. $msg .= "\n". trim(sprintf('#%06d %s', $err['code'], $err['message']));
  696. }
  697. else
  698. {
  699. $msg .= " [". $this->debug_find_source() ."]";
  700. }
  701. trigger_error($msg, E_USER_ERROR);
  702. }
  703. }
  704. /**
  705. * Find caller source
  706. */
  707. function debug_find_source ($mode = '')
  708. {
  709. foreach (debug_backtrace() as $trace)
  710. {
  711. if (!empty($trace['file']) && $trace['file'] !== __FILE__)
  712. {
  713. switch ($mode)
  714. {
  715. case 'file': return $trace['file'];
  716. case 'line': return $trace['line'];
  717. default: return hide_bb_path($trace['file']) .'('. $trace['line'] .')';
  718. }
  719. }
  720. }
  721. return '';
  722. }
  723. /**
  724. * Prepare for logging
  725. */
  726. function log_next_query ($queries_count = 1, $log_file = 'sql_queries')
  727. {
  728. $this->DBS['log_file'] = $log_file;
  729. $this->DBS['log_counter'] = $queries_count;
  730. }
  731. /**
  732. * Log query
  733. */
  734. function log_query ($log_file = 'sql_queries')
  735. {
  736. $q_time = ($this->cur_query_time >= 10) ? round($this->cur_query_time, 0) : sprintf('%.4f', $this->cur_query_time);
  737. $msg = array();
  738. $msg[] = round($this->sql_starttime);
  739. $msg[] = date('m-d H:i:s', $this->sql_starttime);
  740. $msg[] = sprintf('%-6s', $q_time);
  741. $msg[] = sprintf('%-4s', round(sys('la'), 1));
  742. $msg[] = sprintf('%05d', getmypid());
  743. $msg[] = $this->db_server;
  744. $msg[] = short_query($this->cur_query);
  745. $msg = join(LOG_SEPR, $msg);
  746. $msg .= ($info = $this->query_info()) ? ' # '. $info : '';
  747. $msg .= ' # '. $this->debug_find_source() .' ';
  748. $msg .= defined('IN_CRON') ? 'cron' : basename($_SERVER['REQUEST_URI']);
  749. bb_log($msg . LOG_LF, $log_file);
  750. }
  751. /**
  752. * Log slow query
  753. */
  754. function log_slow_query ($log_file = 'sql_slow_bb')
  755. {
  756. if (!defined('IN_FIRST_SLOW_QUERY') && CACHE('bb_cache')->get('dont_log_slow_query'))
  757. {
  758. return;
  759. }
  760. $this->log_query($log_file);
  761. }
  762. /**
  763. * Log error
  764. */
  765. function log_error ()
  766. {
  767. if (!SQL_LOG_ERRORS) return;
  768. $msg = array();
  769. $err = $this->sql_error();
  770. $msg[] = str_compact(sprintf('#%06d %s', $err['code'], $err['message']));
  771. $msg[] = '';
  772. $msg[] = str_compact($this->cur_query);
  773. $msg[] = '';
  774. $msg[] = 'Source : '. $this->debug_find_source() ." :: $this->db_server.$this->selected_db";
  775. $msg[] = 'IP : '. @$_SERVER['REMOTE_ADDR'];
  776. $msg[] = 'Date : '. date('Y-m-d H:i:s');
  777. $msg[] = 'Agent : '. @$_SERVER['HTTP_USER_AGENT'];
  778. $msg[] = 'Req_URI : '. @$_SERVER['REQUEST_URI'];
  779. $msg[] = 'Referer : '. @$_SERVER['HTTP_REFERER'];
  780. $msg[] = 'Method : '. @$_SERVER['REQUEST_METHOD'];
  781. $msg[] = 'PID : '. sprintf('%05d', getmypid());
  782. $msg[] = 'Request : '. trim(print_r($_REQUEST, true)) . str_repeat('_', 78) . LOG_LF;
  783. $msg[] = '';
  784. bb_log($msg, 'sql_error_bb');
  785. }
  786. /**
  787. * Explain queries (based on code from phpBB3)
  788. */
  789. function explain ($mode, $html_table = '', $row = '')
  790. {
  791. $query = str_compact($this->cur_query);
  792. // remove comments
  793. $query = preg_replace('#(\s*)(/\*)(.*)(\*/)(\s*)#', '', $query);
  794. switch ($mode)
  795. {
  796. case 'start':
  797. $this->explain_hold = '';
  798. // TODO: ???????? ????????? ?????????????? ????????
  799. if (preg_match('#UPDATE ([a-z0-9_]+).*?WHERE(.*)/#', $query, $m))
  800. {
  801. $query = "SELECT * FROM $m[1] WHERE $m[2]";
  802. }
  803. else if (preg_match('#DELETE FROM ([a-z0-9_]+).*?WHERE(.*)#s', $query, $m))
  804. {
  805. $query = "SELECT * FROM $m[1] WHERE $m[2]";
  806. }
  807. if (preg_match('#^SELECT#', $query))
  808. {
  809. $html_table = false;
  810. if ($result = @mysql_query("EXPLAIN $query", $this->link))
  811. {
  812. while ($row = @mysql_fetch_assoc($result))
  813. {
  814. $html_table = $this->explain('add_explain_row', $html_table, $row);
  815. }
  816. }
  817. if ($html_table)
  818. {
  819. $this->explain_hold .= '</table>';
  820. }
  821. }
  822. break;
  823. case 'stop':
  824. if (!$this->explain_hold) break;
  825. $id = $this->dbg_id-1;
  826. $htid = 'expl-'. intval($this->link) .'-'. $id;
  827. $dbg = $this->dbg[$id];
  828. $file = addslashes($dbg['file']);
  829. $line = $dbg['line'];
  830. $edit = (DEBUG === true) ? "OpenInEditor('$file', $line);" : '';
  831. $this->explain_out .= '
  832. <table width="98%" cellpadding="0" cellspacing="0" class="bodyline row2 bCenter" style="border-bottom: 0px;">
  833. <tr>
  834. <th style="height: 22px; cursor: pointer;" align="left" title="Open in editor (double click)" ondblclick="'. $edit .'">&nbsp;'. $dbg['src'] .'&nbsp; ['. sprintf('%.4f', $dbg['time']) .' s]&nbsp; <i>'. $dbg['info'] .'</i></th>
  835. <th style="height: 22px; cursor: pointer;" align="right" title="Copy to clipboard" onclick="$.copyToClipboard( $(\'#'. $htid .'\').text() );">'. "$this->db_server.$this->selected_db" .' :: Query #'. ($this->num_queries+1) .'&nbsp;</th>
  836. </tr>
  837. <tr><td colspan="2">'. $this->explain_hold .'</td></tr>
  838. </table>
  839. <div class="sqlLog"><div id="'. $htid .'" class="sqlLogRow sqlExplain" style="padding: 0px;">'. short_query($dbg['sql'], true) .'&nbsp;&nbsp;'. (UA_IE ? '<br /><br />' : '') .'</div></div>
  840. <br />';
  841. break;
  842. case 'add_explain_row':
  843. if (!$html_table && $row)
  844. {
  845. $html_table = true;
  846. $this->explain_hold .= '<table width="100%" cellpadding="3" cellspacing="1" class="bodyline" style="border-width: 0px;"><tr>';
  847. foreach (array_keys($row) as $val)
  848. {
  849. $this->explain_hold .= '<td class="row3 gensmall" align="center"><b>'. $val .'</b></td>';
  850. }
  851. $this->explain_hold .= '</tr>';
  852. }
  853. $this->explain_hold .= '<tr>';
  854. foreach (array_values($row) as $i => $val)
  855. {
  856. $class = !($i % 2) ? 'row1' : 'row2';
  857. $this->explain_hold .= '<td class="'. $class .' gen">'. str_replace(array("{$this->selected_db}.", ',', ';'), array('', ', ', ';<br />'), $val) .'</td>';
  858. }
  859. $this->explain_hold .= '</tr>';
  860. return $html_table;
  861. break;
  862. case 'display':
  863. echo '<a name="explain"></a><div class="med">'. $this->explain_out .'</div>';
  864. break;
  865. }
  866. }
  867. }