<?php if (!defined('IN_DISCUZ')) { exit('Access Denied'); } class db_driver_mysql { var $tablepre; var $version = ''; var $drivertype = 'mysql'; var $querynum = 0; var $slaveid = 0; var $curlink; var $link = array(); var $config = array(); var $sqldebug = array(); var $map = array(); function db_mysql($config = array()) { if (!empty($config)) { $this->set_config($config); } } function set_config($config) { $this->config = &$config; $this->tablepre = $config['1']['tablepre']; if (!empty($this->config['map'])) { $this->map = $this->config['map']; for ($i=1; $i<=100; $i++) { if (isset($this->map['forum_thread'])) { $this->map['forum_thread_'.$i] = $this->map['forum_thread']; } if (isset($this->map['forum_post'])) { $this->map['forum_post_'.$i] = $this->map['forum_post']; } if (isset($this->map['forum_attachment']) && $i <= 10) { $this->map['forum_attachment_'.($i-1)] = $this->map['forum_attachment']; } } if (isset($this->map['common_member'])) { $this->map['common_member_archive'] = $this->map['common_member_count'] = $this->map['common_member_count_archive'] = $this->map['common_member_status'] = $this->map['common_member_status_archive'] = $this->map['common_member_profile'] = $this->map['common_member_profile_archive'] = $this->map['common_member_field_forum'] = $this->map['common_member_field_forum_archive'] = $this->map['common_member_field_home'] = $this->map['common_member_field_home_archive'] = $this->map['common_member_validate'] = $this->map['common_member_verify'] = $this->map['common_member_verify_info'] = $this->map['common_member']; } } } function connect($serverid=1) { if (empty($this->config) || empty($this->config[$serverid])) { $this->halt('config_db_not_found'); } $this->link[$serverid] = $this->_dbconnect( $this->config[$serverid]['dbhost'], $this->config[$serverid]['dbuser'], $this->config[$serverid]['dbpw'], $this->config[$serverid]['dbcharset'], $this->config[$serverid]['dbname'], $this->config[$serverid]['pconnect'] ); $this->curlink = $this->link[$serverid]; } function _dbconnect($dbhost, $dbuser, $dbpw, $dbcharset, $dbname, $pconnect, $halt=true) { if ($pconnect) { $link = @mysql_pconnect($dbhost, $dbuser, $dbpw, MYSQL_CLIENT_COMPRESS); } else { //resource mysql_connect([string $server [,string $username [,string $password [,bool $new_link [,int $client_flags]]]]]) //如果用同样的参数第二次调用 mysql_connect(),将不会建立新连接,而将返回已经打开的连接标识。参数 new_link 改变此行为并使 mysql_connect() 总是打开新的连接,甚至当 mysql_connect() 曾在前面被用同样的参数调用过。 //MYSQL_CLIENT_COMPRESS 使用压缩的通讯协议 $link = @mysql_connect($dbhost, $dbuser, $dbpw, 1, MYSQL_CLIENT_COMPRESS); } if (!$link) { $halt && $this->halt('notconnect', $this->errno()); } else { $this->curlink = $link; if ($this->version() > '4.1') { $dbcharset = $dbcharset ? $dbcharset : $this->config[1]['dbcharset']; $serverset = $dbcharset ? 'character_set_connection='.$dbcharset.', character_set_results='.$dbcharset.', character_set_client=binary' : ''; $serverset .= $this->version() > '5.0.1' ? ((empty($serverset) ? '' : ',').'sql_mode=\'\'') : ''; $serverset && mysql_query("SET $serverset", $link); } $dbname && @mysql_select_db($dbname, $link); } return $link; } function table_name($tablename) { if(!empty($this->map) && !empty($this->map[$tablename])) { $id = $this->map[$tablename]; if(!$this->link[$id]) { $this->connect($id); } $this->curlink = $this->link[$id]; } else { $this->curlink = $this->link[1]; } return $this->tablepre.$tablename; } function select_db($dbname) { return mysql_select_db($dbname, $this->curlink); } //处理SELECT查询结果的主要工具是mysql_fetch_array(), 它带有一个查询结果变量(我曾称之为$result), 并以数组格式一次返回一行数据。 //你将希望在一个循环内使用这个函数,只要有更多的行,循环就会持续访问返回的每一行。从查询读取每条记录的基本构造如下: //while($row = mysql_fetch_array($result)){//do something with $row} //mysql_fetch_array()函数带有一个可选的参数, 用于指定返回的数组的类型: 联合数组、索引数组或者这两者。联合数组允许通过名称引用列值, 而索引数组则要求只使用数字(对于返回的第一列, 从0开始)。 function fetch_array($query, $result_type=MYSQL_ASSOC) { return mysql_fetch_array($query, $result_type); } //array mysql_fetch_row(resource $result) //mysql_fetch_row()从和指定的结果标识关联的结果集中取得一行数据并作为数组返回。每个结果的列储存在一个数组的单元中,偏移量从0开始。 //依次调用mysql_fetch_row()将返回结果集中的下一行,如果没有更多行则返回FALSE。 function fetch_row($query) { $query = mysql_fetch_row($query); return $query; } function fetch_first($sql) { return $this->fetch_array($this->query($sql)); } //一旦你成功地连接到并选择一个数据库,就可以开始执行查询。这些查询可以是诸如插入、更新和删除之类的基本查询,或者是返回许多行的复杂联结中所涉及的查询。 //无论如何,用于执行查询的PHP函数都是mysql_query(): //$result = mysql_query($query); //对于像INSERT、UPDATE、DELETE等简单的查询(它们不会返回记录),$result变量将返回TRUE或FALSE,这取决于查询是否执行成功。对于确实会返回记录的复杂查询(SELECT、SHOW、DESCRIBE和EXPLAIN),如果查询有效,则$result变量将是一个指向查询结果的资源链接:如果查询无效,则$result变量将为FALSE。 public function query($sql, $silent=false, $unbuffered=false) { if(defined('DISCUZ_DEBUG') && DISCUZ_DEBUG) { //mixed microtime([bool $get_as_float]) //microtime()当前Unix时间戳以及微秒数。本函数仅在支持gettimeofday()系统调用的操作系统下可用。 //如果调用时不带可选参数,本函数以 "msec sec" 的格式返回一个字符串,其中 sec 是自Unix纪元(0:00:00 January 1, 1970 GMT)起到现在的秒数,msec是微秒部分。字符串的两部分都是以秒为单位返回的。 //如果给出了 get_as_float 参数并且其值等价于TRUE,microtime()将返回一个浮点数。 $starttime = microtime(true); } //resource mysql_unbuffered_query(string $query [,resource $link_identifier]) //mysql_unbuffered_query()向MYSQL发送一条SQL查询query,但不像mysql_query()那样自动获取并缓存结果集。一方面,这在处理很大的结果集时会节省可观的内存。另一方面,可以在获取第一行后立即对结果集进行操作,而不用等到整个SQL语句都执行完毕。当使用多个数据库连接时,必须指定可选参数link_identifier。 //mysql_unbuffered_query()的好处是有代价的:在mysql_unbuffered_query()返回的结果集之上不能使用mysql_num_rows()和mysql_data_seek()。此外在向MySQL发送一条新的SQL查询之前,必须提取掉所有未缓存的SQL查询所产生的结果行。 if('UNBUFFERED' === $silent) { $silent = false; $unbuffered = true; }elseif('SILENT' === $silent) { $silent = true; $unbuffered = false; } $func = $unbuffered ? 'mysql_unbuffered_query' : 'mysql_query'; //resource mysql_query(string $query [,resource $link_identifier = NULL]) //mysql_query()向与指定的 link_identifier 关联的服务器中的当前活动数据库发送一条查询(不支持多条查询)。 //参数query: SQL查询语句 //参数link_identifier: MySQL连接。如不指定连接标识,则使用由mysql_connect()最近打开的连接。如果没有找到该连接,会尝试不带参数调用mysql_connect()来创建。如没有找到连接或无法建立连接,则会生成 E_WARNING 级别的错误。 //返回值: mysql_query()仅对SELECT, SHOW, DESCRIBE, EXPLAIN和其他语句返回一个resource, 如果查询出现错误则返回FALSE。 //对于其它类型的SQL语句,比如INSERT, UPDATE, DELETE, DROP之类,mysql_query()在执行成功时返回TRUE,出错时返回FALSE。 //返回的结果资源应该传递给mysql_fetch_array()和其他函数来处理结果表,取出返回的数据。 //假定查询成功,可以调用mysql_num_rows()来查看对应于SELECT语句返回了多少行,或者调用mysql_affected_rows()来查看对应于DELETE, INSERT, REPLACE, UPDATE语句影响到了多少行。 //如果没有权限访问查询语句中引用的表时,mysql_query()也会返回FALSE。 if(!($query = $func($sql, $this->curlink))) { //Error: 2013 (CR_SERVER_LOST) //Message: Lost connection to MySQL server during query //Error: 2006 (CR_SERVER_GONE_ERROR) //Message: MySQL server has gone away if(in_array($this->errno(), array(2006, 2013)) && substr($silent, 0, 5) != 'RETRY') { $this->connect(); return $this->query($sql, 'RETRY'.$silent); } if(!$silent) { $this->halt($this->error(), $this->errno(), $sql); } } if(defined('DISCUZ_DEBUG') && DISCUZ_DEBUG) { $this->sqldebug[] = array($sql, number_format((microtime(true)-$starttime), 6), debug_backtrace(), $this->curlink); } $this->querynum++; return $query; } //string mysql_error([resource $link_identifier]) //返回上一个MySQL函数的错误文本,如果没有出错则返回""(空字符串)。如果没有指定连接资源号,则使用上一个成功打开的连接从MySQL服务器提取错误信息。 //从MySQL数据库后端来的错误不再发出警告,要用mysql_error()来提取错误文本。注意本函数仅返回最近一次MySQL函数的执行(不包括mysql_error()和mysql_errno())的错误文本,因此如果要使用此函数,确保在调用另一个MySQL函数之前检查它的值。 function error() { return (($this->curlink) ? mysql_error($this->curlink) : mysql_error()); } //int mysql_errno([resource $link_identifier]) //返回上一个MySQL函数的错误号码,如果没有出错则返回0。 //从MySQL数据库后端来的错误不再发出警告,要用mysql_errno()来提取错误代码。注意本函数仅返回最近一次MySQL函数的执行(不包括mysql_error()和mysql_errno())的错误代码,因此如果要使用此函数,确保在调用另一个MySQL函数之前检查它的值。 //如果指定了可选参数则用给定的连接提取错误代码。否则使用上一个打开的连接。 function errno() { return intval(($this->curlink) ? mysql_errno($this->curlink) : mysql_errno()); } //int mysql_num_rows(resource $result) //mysql_num_rows()返回结果集中行的数目。此命令仅对SELECT语句有效。要取得被INSERT, UPDATE或者DELETE查询所影响到的行的数目用mysql_affected_rows()。 function num_rows($query) { $query = mysql_num_rows($query); return $query; } //int mysql_affected_rows([resource $link_identifier = NULL]) //取得最近一次与link_identifier关联的INSERT, UPDATE或DELETE查询所影响的记录行数。 function affected_rows() { return mysql_affected_rows($this->curlink); } //bool mysql_free_result(resource $result) //mysql_free_result()将释放所有与结果标识符result所关联的内存。 //mysql_free_result()仅需要在考虑到返回很大的结果集时会占用多少内存时调用。在脚本结束后所有关联的内存都会被自动释放。 //成功时返回TRUE,或者在失败时返回FALSE function free_result($query) { return mysql_free_result($query); } //mixed mysql_result(resource $result, int $row [,mixed $field]) //mysql_result()返回MySQL结果集中一个单元的内容。字段参数可以是字段的偏移量或者字段名,或者是字段表点字段名(tablename.fieldname)。如果给列起了别名,则用别名替代列名。 function result($query, $row=0) { $query = @mysql_result($query, $row); return $query; } function result_first($sql) { return $this->result($this->query($sql), 0); } //int mysql_num_fields(resource $result) //mysql_num_fields()返回结果集中字段的数目 function num_fields($query) { return mysql_num_fields($query); } //int mysql_insert_id([resource $link_identifier]) //mysql_insert_id()返回给定的 link_identifier 中上一步INSERT查询中产生的AUTO_INCREMENT的ID号。如果没有指定link_identifier,则使用上一个打开的连接。 //如果上一查询没有产生AUTO_INCREMENT的值,则mysql_insert_id()返回0。如果需要保存该值以后使用,要确保在产生了值的查询之后立即调用mysql_insert_id()。 function insert_id() { return ($id = mysql_insert_id($this->curlink)) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0); } //object mysql_fetch_field(resource $result [,int $field_offset]) //mysql_fetch_field()可以用来从某个查询结果中取得字段的信息。如果没有指定字段偏移量,则下一个尚未被mysql_fetch_field()取得的字段被提取。 function fetch_fields($query) { return mysql_fetch_field($query); } //string mysql_get_server_info([resource $link_identifier]) //mysql_get_server_info()返回link_identifier所使用的服务器版本。如果省略link_identifier,则使用上一个打开的连接。 function version() { if(empty($this->version)) { $this->version = mysql_get_server_info($this->curlink); } return $this->version; } //string mysql_escape_string(string $unescaped_string) //本函数将unescaped_string转义,使之可以安全用于mysql_query() //mysql_escape_string()并不转义%和_,本函数和mysql_real_escape_string()完全一样,除了mysql_real_escape_string()接受的是一个连接句柄并根据当前字符集转移字符串之外。mysql_escape_string()并不接受连接参数,也不管当前字符集设定。 function escape_string($str) { return mysql_escape_string($str); } function close() { return mysql_close($this->curlink); } function halt($message='', $code=0, $sql='') { throw new DbException($message, $code, $sql); } } ?>