diff --git a/html/inc/boinc_db.inc b/html/inc/boinc_db.inc index 63ca43d5b4..ff3b4c20d4 100644 --- a/html/inc/boinc_db.inc +++ b/html/inc/boinc_db.inc @@ -1,7 +1,7 @@ . +// A project can have one or more BOINC databases: +// DB 0: +// the main DB; read/write +// identified in config file by db_host, db_name, db_user, db_passwd +// db_host defaults to localhost +// DB 1: +// read-only replica; identified by +// replica_db_host/name/user/passwd (must include all) +// DB 2: +// read-only replica; identified by +// replica2_db_host/name/user/passwd (must include all) +// ... and potentially more + function incs() { $d = dirname(__FILE__); require_once("$d/db_conn.inc"); @@ -24,122 +37,102 @@ function incs() { incs(); -class BoincDb extends DbConn { - static $instance; +// class BoincDb represents a connection to a BOINC database. +// All its members are static, so there's only 1 connection at a time. +// get(n) establishes a connection to DB n, +// or DB 0 if that fails or doesn't exit. +// close() closes the connection. - // connect to the database (possibly to a read-only replica) - // NOTE: choice of replica can be made only at the page level. - // If there's a page that's guaranteed to do only reads, put - // BoincDb::get(true); - // at the top of it. - // - // Specify a $fallback_mode that is used when $readonly is true: - // 0: default, use db_user if no replica_db_user is specified, - // first try replica_db_host (if specified) then db_host - // 1: only use replica_db_user, first try replica_db_host then db_host - // 2: only use replica_db_user, only try replica_db_host - // can be set projectwide using +class BoincDb { + static $instance; // a DbConn object, or null + static $dbnum; // which replica we're connected to + + // connect to DB $dbnum (0, 1, ...) + // If the requested DB doesn't exist or connection fails, connect to DB 0. + // Set self::$instance; no return value // - static function get_aux($readonly, $fallback_mode = 0) { - $config = get_config(); - $user = parse_config($config, ''); - $passwd = parse_config($config, ''); - $host = parse_config($config, ''); - $replica_host = parse_config($config, ''); - $name = parse_config($config, ''); - $fm = parse_config($config, ''); - if ($fm) { - // override parameter with config.xml setting - $fallback_mode = $fm; - } - if ($host == null) { - $host = "localhost"; - } + static function get_aux($dbnum) { $instance = new DbConn(); - if ($readonly) { - if (($fallback_mode > 0) && (!$replica_host)) { - error_log("BoincDb::get_aux(): required for \$fallback_mode > 0 (giving up)"); - $instance = null; - self::$instance = $instance; - return $instance; - } - $u = parse_config($config, ''); - $p = parse_config($config, ''); - $n = parse_config($config, ''); - if (($fallback_mode > 0) && (!$u || !$p || !$n)) { - error_log("BoincDb::get_aux(): required for \$fallback_mode > 0 (giving up)"); - $instance = null; - self::$instance = $instance; - return $instance; - } else { - // use replica user if given or use normal user for $fallback_mode == 0 - if ($u) $user = $u; - if ($p) $passwd = $p; - if ($n) $name = $n; - } - // skip this block if no $replica_host is specified for $fallback_mode == 0 - if ($replica_host) { - $retval = $instance->init_conn( - $user, $passwd, $replica_host, $name, true - ); + self::$instance = null; + $config = get_config(); + if ($dbnum) { + $r = $dbnum==1?'':strval($dbnum); + $host = parse_config($config, sprintf('', $r)); + $name = parse_config($config, sprintf('', $r)); + $user = parse_config($config, sprintf('', $r)); + $passwd = parse_config($config, sprintf('', $r)); + if ($host && $name && $user && $passwd) { + $retval = $instance->init_conn($user, $passwd, $host, $name); if ($retval) { - // needed for places where we do direct queries - if (!$instance->do_query("use $name")) { - error_log("BoincDb::get_aux(): Couldn't select database $name on $replica_host (giving up)"); - $instance = null; - } - self::$instance = $instance; - return $instance; - } elseif ($fallback_mode == 2) { - // no fallback to master in this case - error_log("BoincDb::get_aux(): Couldn't connect to $user@$replica_host (giving up)"); - $instance = null; + //error_log("BoincDb::get_aux(): connected to replica DB $dbnum"); self::$instance = $instance; - return $instance; - } else { - error_log("BoincDb::get_aux(): Couldn't connect to $user@$replica_host (trying $user@$host next)"); + self::$dbnum = $dbnum; + return; } } + // if can't connect to replica, fall through and try DB 0 } - $retval = $instance->init_conn($user, $passwd, $host, $name, false); - if (!$retval) { - $instance = null; - error_log("BoincDb::get_aux(): Couldn't connect to $user@$host (giving up)"); - } else { - // needed for places where we do direct queries - if (!$instance->do_query("use $name")) { - error_log("BoincDb::get_aux(): Couldn't select database $name on $host (giving up)"); - $instance = null; - } + $host = parse_config($config, ''); + if (!$host) $host = 'localhost'; + $user = parse_config($config, ''); + $name = parse_config($config, ''); + $passwd = parse_config($config, ''); + if (!$name || !$user || !$passwd) { + error_log("BoincDb::get_aux(): must specify DB name, user, passwd"); + return; + } + $retval = $instance->init_conn($user, $passwd, $host, $name); + if ($retval) { + //error_log("BoincDb::get_aux(): connected to DB $dbnum"); + self::$instance = $instance; + self::$dbnum = 0; + return; } - self::$instance = $instance; - return $instance; + error_log("BoincDb::get_aux(): Couldn't connect to DB $dbnum"); } - // same, but + // connect to DB $dbnum, but first: // 1) check for a cached connection // 2) check whether the "stop_web" trigger file is present // - static function get($readonly = false, $fallback_mode = 0) { + // If there's a page that's guaranteed to do only reads, put + // BoincDb::get(true); + // at the top of it. + // + // Note: true == 1. + // You can also use 2, 3... to select other replicas + // + static function get($dbnum = 0) { global $generating_xml; - if (!isset(self::$instance)) { - if (web_stopped()) { - if ($generating_xml) { - xml_error(-183, "project down for maintenance"); - } else { - show_project_down(); - } + if (isset(self::$instance)) { + if (self::$dbnum == $dbnum) { + return self::$instance; } - self::get_aux($readonly, $fallback_mode); - if (!self::$instance) { - if ($generating_xml) { - xml_error(-138, "Can't connect to database"); - } else { - error_page("Can't connect to database"); - } + close(); + } + if (web_stopped()) { + if ($generating_xml) { + xml_error(-183, "project down for maintenance"); + } else { + show_project_down(); } } - return self::$instance; + self::get_aux($dbnum); + if (self::$instance) { + return self::$instance; + } + if ($generating_xml) { + xml_error(-138, "Can't connect to database"); + } else { + error_page("Can't connect to database"); + } + } + + static function close() { + if (isset(self::$instance)) { + self::$instance->close(); + self::$instance = null; + } } static function escape_string($string) { diff --git a/html/inc/db.inc b/html/inc/db.inc index 0238e80475..1c51d3b5b0 100644 --- a/html/inc/db.inc +++ b/html/inc/db.inc @@ -24,20 +24,7 @@ require_once("../inc/util_basic.inc"); // DEPRECATED; use boinc_db.inc instead. // TODO: replace calls to these functions -// use mysqli if available, -// but let projects not use it if they want -// (put in config.xml) -// -if (parse_bool(get_config(), "no_mysqli")) { - define("MYSQLI", false); -} else { - if (class_exists("mysqli")) { - define("MYSQLI", true); - $mysqli = null; - } else { - define("MYSQLI", false); - } -} +define("MYSQLI", true); if (MYSQLI) { function _mysql_connect($host, $user, $pass, $dbname) { diff --git a/html/inc/db_conn.inc b/html/inc/db_conn.inc index 7eb0fb70d0..b39ae8d36f 100644 --- a/html/inc/db_conn.inc +++ b/html/inc/db_conn.inc @@ -22,42 +22,40 @@ require_once("../inc/db.inc"); // Intended to be subclassed (e.g., BoincDb, BossaDb) // class DbConn { - var $db_conn; - var $db_name; - var $readonly; + var $db_conn; // a mysqli object + var $db_name; // the DB name - function init_conn($user, $passwd, $host, $name, $readonly) { - if (MYSQLI) { - $x = explode(":", $host); - if (sizeof($x)>1) { - $host = $x[0]; - $port = $x[1]; - } else { - $port = null; - } - //if (version_compare(PHP_VERSION, '5.3.0') < 0) { - if (1) { // don't use persistent connections for now - $this->db_conn = @new mysqli($host, $user, $passwd, $name, $port); - } else { - $this->db_conn = @new mysqli("p:".$host, $user, $passwd, $name, $port); - } - // mysqli returns an object even if the connection is not established - if (mysqli_connect_error()) { - return false; - } - global $mysqli; - $mysqli = $this->db_conn; + function init_conn($user, $passwd, $host, $name) { + $x = explode(":", $host); + if (sizeof($x)>1) { + $host = $x[0]; + $port = $x[1]; } else { - $this->db_conn = @mysql_pconnect($host, $user, $passwd); + $port = null; + } + try { + $this->db_conn = @new mysqli($host, $user, $passwd, $name, $port); + } catch(Exception $e) { + return false; } + if (mysqli_connect_error()) { + return false; + } + global $mysqli; + $mysqli = $this->db_conn; if (!$this->db_conn) { return false; } $this->db_name = $name; - $this->readonly = $readonly; return true; } + function close() { + if ($this->db_conn) { + $this->db_conn->close(); + } + } + // in keeping with PHP/MySQL convention, return true (nonzero) on success. // (This is the opposite of the BOINC convention) // @@ -65,11 +63,7 @@ class DbConn { global $generating_xml; $q = str_replace('DBNAME', $this->db_name, $q); //echo "query: $q
\n"; - if (MYSQLI) { - $ret = $this->db_conn->query($q); - } else { - $ret = mysql_query($q, $this->db_conn); - } + $ret = $this->db_conn->query($q); if (!$ret) { if (!$generating_xml) { echo "Database Error
\n"; @@ -85,11 +79,7 @@ class DbConn { // # rows affected by last query // function affected_rows() { - if (MYSQLI) { - return $this->db_conn->affected_rows; - } else { - return mysql_affected_rows($this->db_conn); - } + return $this->db_conn->affected_rows; } function get_list($table1, $table2, $joinfield1, $joinfield2, $classname, $fields, $where_clause, $order_clause, $limit) { @@ -97,17 +87,10 @@ class DbConn { $result = $this->do_query($query); if (!$result) return null; $x = array(); - if (MYSQLI) { - while ($obj = $result->fetch_object($classname)) { - $x[] = $obj; - } - $result->free(); - } else { - while ($obj = mysql_fetch_object($result, $classname)) { - $x[] = $obj; - } - mysql_free_result($result); + while ($obj = $result->fetch_object($classname)) { + $x[] = $obj; } + $result->free(); return $x; } @@ -117,13 +100,8 @@ class DbConn { if (!$result) { return null; } - if (MYSQLI) { - $obj = $result->fetch_object($classname); - $result->free(); - } else { - $obj = mysql_fetch_object($result, $classname); - mysql_free_result($result); - } + $obj = $result->fetch_object($classname); + $result->free(); return $obj; } @@ -140,17 +118,10 @@ class DbConn { $result = $this->do_query($query); if (!$result) return null; $x = array(); - if (MYSQLI) { - while ($obj = $result->fetch_object($classname)) { - $x[] = $obj; - } - $result->free(); - } else { - while ($obj = mysql_fetch_object($result, $classname)) { - $x[] = $obj; - } - mysql_free_result($result); + while ($obj = $result->fetch_object($classname)) { + $x[] = $obj; } + $result->free(); return $x; } @@ -192,35 +163,21 @@ class DbConn { return $this->do_query($query); } function insert_id() { - if (MYSQLI) { - return $this->db_conn->insert_id; - } else { - return mysql_insert_id($this->db_conn); - } + return $this->db_conn->insert_id; } function get_int($query, $field) { $result = $this->do_query($query); if (!$result) error_page("database error on query $query"); - if (MYSQLI) { - $x = $result->fetch_object("StdClass"); - $result->free(); - } else { - $x = mysql_fetch_object($result); - mysql_free_result($result); - } + $x = $result->fetch_object("StdClass"); + $result->free(); if ($x) return $x->$field; return false; } function get_double($query, $field) { $result = $this->do_query($query); if (!$result) error_page("database error on query $query"); - if (MYSQLI) { - $x = $result->fetch_object("StdClass"); - $result->free(); - } else { - $x = mysql_fetch_object($result); - mysql_free_result($result); - } + $x = $result->fetch_object("StdClass"); + $result->free(); if ($x) return (double)$x->$field; return false; } @@ -248,25 +205,13 @@ class DbConn { return $this->do_query($query); } function base_escape_string($string) { - if (MYSQLI) { - return $this->db_conn->escape_string($string); - } else { - return mysql_real_escape_string($string); - } + return $this->db_conn->escape_string($string); } function base_error() { - if (MYSQLI) { - return $this->db_conn->error; - } else { - return mysql_error($this->db_conn); - } + return $this->db_conn->error; } function base_errno() { - if (MYSQLI) { - return $this->db_conn->errno; - } else { - return mysql_errno($this->db_conn); - } + return $this->db_conn->errno; } function table_exists($table_name) { $result = $this->do_query("show tables from DBNAME like '$table_name'");