| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059 | 
							- <?php
 
- // +----------------------------------------------------------------------
 
- // | ThinkPHP [ WE CAN DO IT JUST THINK ]
 
- // +----------------------------------------------------------------------
 
- // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
 
- // +----------------------------------------------------------------------
 
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
 
- // +----------------------------------------------------------------------
 
- // | Author: liu21st <liu21st@gmail.com>
 
- // +----------------------------------------------------------------------
 
- namespace think\db;
 
- use PDO;
 
- use PDOStatement;
 
- use think\Db;
 
- use think\db\exception\BindParamException;
 
- use think\Debug;
 
- use think\Exception;
 
- use think\exception\PDOException;
 
- use think\Log;
 
- /**
 
-  * Class Connection
 
-  * @package think
 
-  * @method Query table(string $table) 指定数据表(含前缀)
 
-  * @method Query name(string $name) 指定数据表(不含前缀)
 
-  *
 
-  */
 
- abstract class Connection
 
- {
 
-     /** @var PDOStatement PDO操作实例 */
 
-     protected $PDOStatement;
 
-     /** @var string 当前SQL指令 */
 
-     protected $queryStr = '';
 
-     // 返回或者影响记录数
 
-     protected $numRows = 0;
 
-     // 事务指令数
 
-     protected $transTimes = 0;
 
-     // 错误信息
 
-     protected $error = '';
 
-     /** @var PDO[] 数据库连接ID 支持多个连接 */
 
-     protected $links = [];
 
-     /** @var PDO 当前连接ID */
 
-     protected $linkID;
 
-     protected $linkRead;
 
-     protected $linkWrite;
 
-     // 查询结果类型
 
-     protected $fetchType = PDO::FETCH_ASSOC;
 
-     // 字段属性大小写
 
-     protected $attrCase = PDO::CASE_LOWER;
 
-     // 监听回调
 
-     protected static $event = [];
 
-     // 使用Builder类
 
-     protected $builder;
 
-     // 数据库连接参数配置
 
-     protected $config = [
 
-         // 数据库类型
 
-         'type'            => '',
 
-         // 服务器地址
 
-         'hostname'        => '',
 
-         // 数据库名
 
-         'database'        => '',
 
-         // 用户名
 
-         'username'        => '',
 
-         // 密码
 
-         'password'        => '',
 
-         // 端口
 
-         'hostport'        => '',
 
-         // 连接dsn
 
-         'dsn'             => '',
 
-         // 数据库连接参数
 
-         'params'          => [],
 
-         // 数据库编码默认采用utf8
 
-         'charset'         => 'utf8',
 
-         // 数据库表前缀
 
-         'prefix'          => '',
 
-         // 数据库调试模式
 
-         'debug'           => false,
 
-         // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
 
-         'deploy'          => 0,
 
-         // 数据库读写是否分离 主从式有效
 
-         'rw_separate'     => false,
 
-         // 读写分离后 主服务器数量
 
-         'master_num'      => 1,
 
-         // 指定从服务器序号
 
-         'slave_no'        => '',
 
-         // 模型写入后自动读取主服务器
 
-         'read_master'     => false,
 
-         // 是否严格检查字段是否存在
 
-         'fields_strict'   => true,
 
-         // 数据返回类型
 
-         'result_type'     => PDO::FETCH_ASSOC,
 
-         // 数据集返回类型
 
-         'resultset_type'  => 'array',
 
-         // 自动写入时间戳字段
 
-         'auto_timestamp'  => false,
 
-         // 时间字段取出后的默认时间格式
 
-         'datetime_format' => 'Y-m-d H:i:s',
 
-         // 是否需要进行SQL性能分析
 
-         'sql_explain'     => false,
 
-         // Builder类
 
-         'builder'         => '',
 
-         // Query类
 
-         'query'           => '\\think\\db\\Query',
 
-         // 是否需要断线重连
 
-         'break_reconnect' => false,
 
-     ];
 
-     // PDO连接参数
 
-     protected $params = [
 
-         PDO::ATTR_CASE              => PDO::CASE_NATURAL,
 
-         PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
 
-         PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
 
-         PDO::ATTR_STRINGIFY_FETCHES => false,
 
-         PDO::ATTR_EMULATE_PREPARES  => false,
 
-     ];
 
-     // 绑定参数
 
-     protected $bind = [];
 
-     /**
 
-      * 构造函数 读取数据库配置信息
 
-      * @access public
 
-      * @param array $config 数据库配置数组
 
-      */
 
-     public function __construct(array $config = [])
 
-     {
 
-         if (!empty($config)) {
 
-             $this->config = array_merge($this->config, $config);
 
-         }
 
-     }
 
-     /**
 
-      * 获取新的查询对象
 
-      * @access protected
 
-      * @return Query
 
-      */
 
-     protected function getQuery()
 
-     {
 
-         $class = $this->config['query'];
 
-         return new $class($this);
 
-     }
 
-     /**
 
-      * 获取当前连接器类对应的Builder类
 
-      * @access public
 
-      * @return string
 
-      */
 
-     public function getBuilder()
 
-     {
 
-         if (!empty($this->builder)) {
 
-             return $this->builder;
 
-         } else {
 
-             return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type'));
 
-         }
 
-     }
 
-     /**
 
-      * 调用Query类的查询方法
 
-      * @access public
 
-      * @param string    $method 方法名称
 
-      * @param array     $args 调用参数
 
-      * @return mixed
 
-      */
 
-     public function __call($method, $args)
 
-     {
 
-         return call_user_func_array([$this->getQuery(), $method], $args);
 
-     }
 
-     /**
 
-      * 解析pdo连接的dsn信息
 
-      * @access protected
 
-      * @param array $config 连接信息
 
-      * @return string
 
-      */
 
-     abstract protected function parseDsn($config);
 
-     /**
 
-      * 取得数据表的字段信息
 
-      * @access public
 
-      * @param string $tableName
 
-      * @return array
 
-      */
 
-     abstract public function getFields($tableName);
 
-     /**
 
-      * 取得数据库的表信息
 
-      * @access public
 
-      * @param string $dbName
 
-      * @return array
 
-      */
 
-     abstract public function getTables($dbName);
 
-     /**
 
-      * SQL性能分析
 
-      * @access protected
 
-      * @param string $sql
 
-      * @return array
 
-      */
 
-     abstract protected function getExplain($sql);
 
-     /**
 
-      * 对返数据表字段信息进行大小写转换出来
 
-      * @access public
 
-      * @param array $info 字段信息
 
-      * @return array
 
-      */
 
-     public function fieldCase($info)
 
-     {
 
-         // 字段大小写转换
 
-         switch ($this->attrCase) {
 
-             case PDO::CASE_LOWER:
 
-                 $info = array_change_key_case($info);
 
-                 break;
 
-             case PDO::CASE_UPPER:
 
-                 $info = array_change_key_case($info, CASE_UPPER);
 
-                 break;
 
-             case PDO::CASE_NATURAL:
 
-             default:
 
-                 // 不做转换
 
-         }
 
-         return $info;
 
-     }
 
-     /**
 
-      * 获取数据库的配置参数
 
-      * @access public
 
-      * @param string $config 配置名称
 
-      * @return mixed
 
-      */
 
-     public function getConfig($config = '')
 
-     {
 
-         return $config ? $this->config[$config] : $this->config;
 
-     }
 
-     /**
 
-      * 设置数据库的配置参数
 
-      * @access public
 
-      * @param string|array      $config 配置名称
 
-      * @param mixed             $value 配置值
 
-      * @return void
 
-      */
 
-     public function setConfig($config, $value = '')
 
-     {
 
-         if (is_array($config)) {
 
-             $this->config = array_merge($this->config, $config);
 
-         } else {
 
-             $this->config[$config] = $value;
 
-         }
 
-     }
 
-     /**
 
-      * 连接数据库方法
 
-      * @access public
 
-      * @param array         $config 连接参数
 
-      * @param integer       $linkNum 连接序号
 
-      * @param array|bool    $autoConnection 是否自动连接主数据库(用于分布式)
 
-      * @return PDO
 
-      * @throws Exception
 
-      */
 
-     public function connect(array $config = [], $linkNum = 0, $autoConnection = false)
 
-     {
 
-         if (!isset($this->links[$linkNum])) {
 
-             if (!$config) {
 
-                 $config = $this->config;
 
-             } else {
 
-                 $config = array_merge($this->config, $config);
 
-             }
 
-             // 连接参数
 
-             if (isset($config['params']) && is_array($config['params'])) {
 
-                 $params = $config['params'] + $this->params;
 
-             } else {
 
-                 $params = $this->params;
 
-             }
 
-             // 记录当前字段属性大小写设置
 
-             $this->attrCase = $params[PDO::ATTR_CASE];
 
-             // 数据返回类型
 
-             if (isset($config['result_type'])) {
 
-                 $this->fetchType = $config['result_type'];
 
-             }
 
-             try {
 
-                 if (empty($config['dsn'])) {
 
-                     $config['dsn'] = $this->parseDsn($config);
 
-                 }
 
-                 if ($config['debug']) {
 
-                     $startTime = microtime(true);
 
-                 }
 
-                 $this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params);
 
-                 if ($config['debug']) {
 
-                     // 记录数据库连接信息
 
-                     Log::record('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn'], 'sql');
 
-                 }
 
-             } catch (\PDOException $e) {
 
-                 if ($autoConnection) {
 
-                     Log::record($e->getMessage(), 'error');
 
-                     return $this->connect($autoConnection, $linkNum);
 
-                 } else {
 
-                     throw $e;
 
-                 }
 
-             }
 
-         }
 
-         return $this->links[$linkNum];
 
-     }
 
-     /**
 
-      * 释放查询结果
 
-      * @access public
 
-      */
 
-     public function free()
 
-     {
 
-         $this->PDOStatement = null;
 
-     }
 
-     /**
 
-      * 获取PDO对象
 
-      * @access public
 
-      * @return \PDO|false
 
-      */
 
-     public function getPdo()
 
-     {
 
-         if (!$this->linkID) {
 
-             return false;
 
-         } else {
 
-             return $this->linkID;
 
-         }
 
-     }
 
-     /**
 
-      * 执行查询 返回数据集
 
-      * @access public
 
-      * @param string        $sql sql指令
 
-      * @param array         $bind 参数绑定
 
-      * @param bool          $master 是否在主服务器读操作
 
-      * @param bool          $pdo 是否返回PDO对象
 
-      * @return mixed
 
-      * @throws PDOException
 
-      * @throws \Exception
 
-      */
 
-     public function query($sql, $bind = [], $master = false, $pdo = false)
 
-     {
 
-         $this->initConnect($master);
 
-         if (!$this->linkID) {
 
-             return false;
 
-         }
 
-         // 记录SQL语句
 
-         $this->queryStr = $sql;
 
-         if ($bind) {
 
-             $this->bind = $bind;
 
-         }
 
-         Db::$queryTimes++;
 
-         try {
 
-             // 调试开始
 
-             $this->debug(true);
 
-             // 预处理
 
-             $this->PDOStatement = $this->linkID->prepare($sql);
 
-             // 是否为存储过程调用
 
-             $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
 
-             // 参数绑定
 
-             if ($procedure) {
 
-                 $this->bindParam($bind);
 
-             } else {
 
-                 $this->bindValue($bind);
 
-             }
 
-             // 执行查询
 
-             $this->PDOStatement->execute();
 
-             // 调试结束
 
-             $this->debug(false, '', $master);
 
-             // 返回结果集
 
-             return $this->getResult($pdo, $procedure);
 
-         } catch (\PDOException $e) {
 
-             if ($this->isBreak($e)) {
 
-                 return $this->close()->query($sql, $bind, $master, $pdo);
 
-             }
 
-             throw new PDOException($e, $this->config, $this->getLastsql());
 
-         } catch (\Throwable $e) {
 
-             if ($this->isBreak($e)) {
 
-                 return $this->close()->query($sql, $bind, $master, $pdo);
 
-             }
 
-             throw $e;
 
-         } catch (\Exception $e) {
 
-             if ($this->isBreak($e)) {
 
-                 return $this->close()->query($sql, $bind, $master, $pdo);
 
-             }
 
-             throw $e;
 
-         }
 
-     }
 
-     /**
 
-      * 执行语句
 
-      * @access public
 
-      * @param  string        $sql sql指令
 
-      * @param  array         $bind 参数绑定
 
-      * @param  Query         $query 查询对象
 
-      * @return int
 
-      * @throws PDOException
 
-      * @throws \Exception
 
-      */
 
-     public function execute($sql, $bind = [], Query $query = null)
 
-     {
 
-         $this->initConnect(true);
 
-         if (!$this->linkID) {
 
-             return false;
 
-         }
 
-         // 记录SQL语句
 
-         $this->queryStr = $sql;
 
-         if ($bind) {
 
-             $this->bind = $bind;
 
-         }
 
-         Db::$executeTimes++;
 
-         try {
 
-             // 调试开始
 
-             $this->debug(true);
 
-             // 预处理
 
-             $this->PDOStatement = $this->linkID->prepare($sql);
 
-             // 是否为存储过程调用
 
-             $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
 
-             // 参数绑定
 
-             if ($procedure) {
 
-                 $this->bindParam($bind);
 
-             } else {
 
-                 $this->bindValue($bind);
 
-             }
 
-             // 执行语句
 
-             $this->PDOStatement->execute();
 
-             // 调试结束
 
-             $this->debug(false, '', true);
 
-             if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) {
 
-                 $query->readMaster();
 
-             }
 
-             $this->numRows = $this->PDOStatement->rowCount();
 
-             return $this->numRows;
 
-         } catch (\PDOException $e) {
 
-             if ($this->isBreak($e)) {
 
-                 return $this->close()->execute($sql, $bind, $query);
 
-             }
 
-             throw new PDOException($e, $this->config, $this->getLastsql());
 
-         } catch (\Throwable $e) {
 
-             if ($this->isBreak($e)) {
 
-                 return $this->close()->execute($sql, $bind, $query);
 
-             }
 
-             throw $e;
 
-         } catch (\Exception $e) {
 
-             if ($this->isBreak($e)) {
 
-                 return $this->close()->execute($sql, $bind, $query);
 
-             }
 
-             throw $e;
 
-         }
 
-     }
 
-     /**
 
-      * 根据参数绑定组装最终的SQL语句 便于调试
 
-      * @access public
 
-      * @param string    $sql 带参数绑定的sql语句
 
-      * @param array     $bind 参数绑定列表
 
-      * @return string
 
-      */
 
-     public function getRealSql($sql, array $bind = [])
 
-     {
 
-         if (is_array($sql)) {
 
-             $sql = implode(';', $sql);
 
-         }
 
-         foreach ($bind as $key => $val) {
 
-             $value = is_array($val) ? $val[0] : $val;
 
-             $type  = is_array($val) ? $val[1] : PDO::PARAM_STR;
 
-             if (PDO::PARAM_STR == $type) {
 
-                 $value = $this->quote($value);
 
-             } elseif (PDO::PARAM_INT == $type) {
 
-                 $value = (float) $value;
 
-             }
 
-             // 判断占位符
 
-             $sql = is_numeric($key) ?
 
-             substr_replace($sql, $value, strpos($sql, '?'), 1) :
 
-             str_replace(
 
-                 [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL],
 
-                 [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL],
 
-                 $sql . ' ');
 
-         }
 
-         return rtrim($sql);
 
-     }
 
-     /**
 
-      * 参数绑定
 
-      * 支持 ['name'=>'value','id'=>123] 对应命名占位符
 
-      * 或者 ['value',123] 对应问号占位符
 
-      * @access public
 
-      * @param array $bind 要绑定的参数列表
 
-      * @return void
 
-      * @throws BindParamException
 
-      */
 
-     protected function bindValue(array $bind = [])
 
-     {
 
-         foreach ($bind as $key => $val) {
 
-             // 占位符
 
-             $param = is_numeric($key) ? $key + 1 : ':' . $key;
 
-             if (is_array($val)) {
 
-                 if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
 
-                     $val[0] = 0;
 
-                 }
 
-                 $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
 
-             } else {
 
-                 $result = $this->PDOStatement->bindValue($param, $val);
 
-             }
 
-             if (!$result) {
 
-                 throw new BindParamException(
 
-                     "Error occurred  when binding parameters '{$param}'",
 
-                     $this->config,
 
-                     $this->getLastsql(),
 
-                     $bind
 
-                 );
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * 存储过程的输入输出参数绑定
 
-      * @access public
 
-      * @param array $bind 要绑定的参数列表
 
-      * @return void
 
-      * @throws BindParamException
 
-      */
 
-     protected function bindParam($bind)
 
-     {
 
-         foreach ($bind as $key => $val) {
 
-             $param = is_numeric($key) ? $key + 1 : ':' . $key;
 
-             if (is_array($val)) {
 
-                 array_unshift($val, $param);
 
-                 $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val);
 
-             } else {
 
-                 $result = $this->PDOStatement->bindValue($param, $val);
 
-             }
 
-             if (!$result) {
 
-                 $param = array_shift($val);
 
-                 throw new BindParamException(
 
-                     "Error occurred  when binding parameters '{$param}'",
 
-                     $this->config,
 
-                     $this->getLastsql(),
 
-                     $bind
 
-                 );
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * 获得数据集数组
 
-      * @access protected
 
-      * @param bool   $pdo 是否返回PDOStatement
 
-      * @param bool   $procedure 是否存储过程
 
-      * @return PDOStatement|array
 
-      */
 
-     protected function getResult($pdo = false, $procedure = false)
 
-     {
 
-         if ($pdo) {
 
-             // 返回PDOStatement对象处理
 
-             return $this->PDOStatement;
 
-         }
 
-         if ($procedure) {
 
-             // 存储过程返回结果
 
-             return $this->procedure();
 
-         }
 
-         $result        = $this->PDOStatement->fetchAll($this->fetchType);
 
-         $this->numRows = count($result);
 
-         return $result;
 
-     }
 
-     /**
 
-      * 获得存储过程数据集
 
-      * @access protected
 
-      * @return array
 
-      */
 
-     protected function procedure()
 
-     {
 
-         $item = [];
 
-         do {
 
-             $result = $this->getResult();
 
-             if ($result) {
 
-                 $item[] = $result;
 
-             }
 
-         } while ($this->PDOStatement->nextRowset());
 
-         $this->numRows = count($item);
 
-         return $item;
 
-     }
 
-     /**
 
-      * 执行数据库事务
 
-      * @access public
 
-      * @param callable $callback 数据操作方法回调
 
-      * @return mixed
 
-      * @throws PDOException
 
-      * @throws \Exception
 
-      * @throws \Throwable
 
-      */
 
-     public function transaction($callback)
 
-     {
 
-         $this->startTrans();
 
-         try {
 
-             $result = null;
 
-             if (is_callable($callback)) {
 
-                 $result = call_user_func_array($callback, [$this]);
 
-             }
 
-             $this->commit();
 
-             return $result;
 
-         } catch (\Exception $e) {
 
-             $this->rollback();
 
-             throw $e;
 
-         } catch (\Throwable $e) {
 
-             $this->rollback();
 
-             throw $e;
 
-         }
 
-     }
 
-     /**
 
-      * 启动事务
 
-      * @access public
 
-      * @return bool|mixed
 
-      * @throws \Exception
 
-      */
 
-     public function startTrans()
 
-     {
 
-         $this->initConnect(true);
 
-         if (!$this->linkID) {
 
-             return false;
 
-         }
 
-         ++$this->transTimes;
 
-         try {
 
-             if (1 == $this->transTimes) {
 
-                 $this->linkID->beginTransaction();
 
-             } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
 
-                 $this->linkID->exec(
 
-                     $this->parseSavepoint('trans' . $this->transTimes)
 
-                 );
 
-             }
 
-         } catch (\Exception $e) {
 
-             if ($this->isBreak($e)) {
 
-                 --$this->transTimes;
 
-                 return $this->close()->startTrans();
 
-             }
 
-             throw $e;
 
-         } catch (\Error $e) {
 
-             if ($this->isBreak($e)) {
 
-                 --$this->transTimes;
 
-                 return $this->close()->startTrans();
 
-             }
 
-             throw $e;
 
-         }
 
-     }
 
-     /**
 
-      * 用于非自动提交状态下面的查询提交
 
-      * @access public
 
-      * @return void
 
-      * @throws PDOException
 
-      */
 
-     public function commit()
 
-     {
 
-         $this->initConnect(true);
 
-         if (1 == $this->transTimes) {
 
-             $this->linkID->commit();
 
-         }
 
-         --$this->transTimes;
 
-     }
 
-     /**
 
-      * 事务回滚
 
-      * @access public
 
-      * @return void
 
-      * @throws PDOException
 
-      */
 
-     public function rollback()
 
-     {
 
-         $this->initConnect(true);
 
-         if (1 == $this->transTimes) {
 
-             $this->linkID->rollBack();
 
-         } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
 
-             $this->linkID->exec(
 
-                 $this->parseSavepointRollBack('trans' . $this->transTimes)
 
-             );
 
-         }
 
-         $this->transTimes = max(0, $this->transTimes - 1);
 
-     }
 
-     /**
 
-      * 是否支持事务嵌套
 
-      * @return bool
 
-      */
 
-     protected function supportSavepoint()
 
-     {
 
-         return false;
 
-     }
 
-     /**
 
-      * 生成定义保存点的SQL
 
-      * @param $name
 
-      * @return string
 
-      */
 
-     protected function parseSavepoint($name)
 
-     {
 
-         return 'SAVEPOINT ' . $name;
 
-     }
 
-     /**
 
-      * 生成回滚到保存点的SQL
 
-      * @param $name
 
-      * @return string
 
-      */
 
-     protected function parseSavepointRollBack($name)
 
-     {
 
-         return 'ROLLBACK TO SAVEPOINT ' . $name;
 
-     }
 
-     /**
 
-      * 批处理执行SQL语句
 
-      * 批处理的指令都认为是execute操作
 
-      * @access public
 
-      * @param array $sqlArray SQL批处理指令
 
-      * @return boolean
 
-      */
 
-     public function batchQuery($sqlArray = [], $bind = [], Query $query = null)
 
-     {
 
-         if (!is_array($sqlArray)) {
 
-             return false;
 
-         }
 
-         // 自动启动事务支持
 
-         $this->startTrans();
 
-         try {
 
-             foreach ($sqlArray as $sql) {
 
-                 $this->execute($sql, $bind, $query);
 
-             }
 
-             // 提交事务
 
-             $this->commit();
 
-         } catch (\Exception $e) {
 
-             $this->rollback();
 
-             throw $e;
 
-         }
 
-         return true;
 
-     }
 
-     /**
 
-      * 获得查询次数
 
-      * @access public
 
-      * @param boolean $execute 是否包含所有查询
 
-      * @return integer
 
-      */
 
-     public function getQueryTimes($execute = false)
 
-     {
 
-         return $execute ? Db::$queryTimes + Db::$executeTimes : Db::$queryTimes;
 
-     }
 
-     /**
 
-      * 获得执行次数
 
-      * @access public
 
-      * @return integer
 
-      */
 
-     public function getExecuteTimes()
 
-     {
 
-         return Db::$executeTimes;
 
-     }
 
-     /**
 
-      * 关闭数据库(或者重新连接)
 
-      * @access public
 
-      * @return $this
 
-      */
 
-     public function close()
 
-     {
 
-         $this->linkID    = null;
 
-         $this->linkWrite = null;
 
-         $this->linkRead  = null;
 
-         $this->links     = [];
 
-         // 释放查询
 
-         $this->free();
 
-         return $this;
 
-     }
 
-     /**
 
-      * 是否断线
 
-      * @access protected
 
-      * @param \PDOException|\Exception  $e 异常对象
 
-      * @return bool
 
-      */
 
-     protected function isBreak($e)
 
-     {
 
-         if (!$this->config['break_reconnect']) {
 
-             return false;
 
-         }
 
-         $info = [
 
-             'server has gone away',
 
-             'no connection to the server',
 
-             'Lost connection',
 
-             'is dead or not enabled',
 
-             'Error while sending',
 
-             'decryption failed or bad record mac',
 
-             'server closed the connection unexpectedly',
 
-             'SSL connection has been closed unexpectedly',
 
-             'Error writing data to the connection',
 
-             'Resource deadlock avoided',
 
-             'failed with errno',
 
-         ];
 
-         $error = $e->getMessage();
 
-         foreach ($info as $msg) {
 
-             if (false !== stripos($error, $msg)) {
 
-                 return true;
 
-             }
 
-         }
 
-         return false;
 
-     }
 
-     /**
 
-      * 获取最近一次查询的sql语句
 
-      * @access public
 
-      * @return string
 
-      */
 
-     public function getLastSql()
 
-     {
 
-         return $this->getRealSql($this->queryStr, $this->bind);
 
-     }
 
-     /**
 
-      * 获取最近插入的ID
 
-      * @access public
 
-      * @param string  $sequence     自增序列名
 
-      * @return string
 
-      */
 
-     public function getLastInsID($sequence = null)
 
-     {
 
-         return $this->linkID->lastInsertId($sequence);
 
-     }
 
-     /**
 
-      * 获取返回或者影响的记录数
 
-      * @access public
 
-      * @return integer
 
-      */
 
-     public function getNumRows()
 
-     {
 
-         return $this->numRows;
 
-     }
 
-     /**
 
-      * 获取最近的错误信息
 
-      * @access public
 
-      * @return string
 
-      */
 
-     public function getError()
 
-     {
 
-         if ($this->PDOStatement) {
 
-             $error = $this->PDOStatement->errorInfo();
 
-             $error = $error[1] . ':' . $error[2];
 
-         } else {
 
-             $error = '';
 
-         }
 
-         if ('' != $this->queryStr) {
 
-             $error .= "\n [ SQL语句 ] : " . $this->getLastsql();
 
-         }
 
-         return $error;
 
-     }
 
-     /**
 
-      * SQL指令安全过滤
 
-      * @access public
 
-      * @param string $str SQL字符串
 
-      * @param bool   $master 是否主库查询
 
-      * @return string
 
-      */
 
-     public function quote($str, $master = true)
 
-     {
 
-         $this->initConnect($master);
 
-         return $this->linkID ? $this->linkID->quote($str) : $str;
 
-     }
 
-     /**
 
-      * 数据库调试 记录当前SQL及分析性能
 
-      * @access protected
 
-      * @param boolean $start 调试开始标记 true 开始 false 结束
 
-      * @param string  $sql 执行的SQL语句 留空自动获取
 
-      * @param boolean $master 主从标记
 
-      * @return void
 
-      */
 
-     protected function debug($start, $sql = '', $master = false)
 
-     {
 
-         if (!empty($this->config['debug'])) {
 
-             // 开启数据库调试模式
 
-             if ($start) {
 
-                 Debug::remark('queryStartTime', 'time');
 
-             } else {
 
-                 // 记录操作结束时间
 
-                 Debug::remark('queryEndTime', 'time');
 
-                 $runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime');
 
-                 $sql     = $sql ?: $this->getLastsql();
 
-                 $result  = [];
 
-                 // SQL性能分析
 
-                 if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) {
 
-                     $result = $this->getExplain($sql);
 
-                 }
 
-                 // SQL监听
 
-                 $this->trigger($sql, $runtime, $result, $master);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * 监听SQL执行
 
-      * @access public
 
-      * @param callable $callback 回调方法
 
-      * @return void
 
-      */
 
-     public function listen($callback)
 
-     {
 
-         self::$event[] = $callback;
 
-     }
 
-     /**
 
-      * 触发SQL事件
 
-      * @access protected
 
-      * @param string    $sql SQL语句
 
-      * @param float     $runtime SQL运行时间
 
-      * @param mixed     $explain SQL分析
 
-      * @param  bool     $master 主从标记
 
-      * @return void
 
-      */
 
-     protected function trigger($sql, $runtime, $explain = [], $master = false)
 
-     {
 
-         if (!empty(self::$event)) {
 
-             foreach (self::$event as $callback) {
 
-                 if (is_callable($callback)) {
 
-                     call_user_func_array($callback, [$sql, $runtime, $explain, $master]);
 
-                 }
 
-             }
 
-         } else {
 
-             // 未注册监听则记录到日志中
 
-             if ($this->config['deploy']) {
 
-                 // 分布式记录当前操作的主从
 
-                 $master = $master ? 'master|' : 'slave|';
 
-             } else {
 
-                 $master = '';
 
-             }
 
-             Log::record('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]', 'sql');
 
-             if (!empty($explain)) {
 
-                 Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql');
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * 初始化数据库连接
 
-      * @access protected
 
-      * @param boolean $master 是否主服务器
 
-      * @return void
 
-      */
 
-     protected function initConnect($master = true)
 
-     {
 
-         if (!empty($this->config['deploy'])) {
 
-             // 采用分布式数据库
 
-             if ($master || $this->transTimes) {
 
-                 if (!$this->linkWrite) {
 
-                     $this->linkWrite = $this->multiConnect(true);
 
-                 }
 
-                 $this->linkID = $this->linkWrite;
 
-             } else {
 
-                 if (!$this->linkRead) {
 
-                     $this->linkRead = $this->multiConnect(false);
 
-                 }
 
-                 $this->linkID = $this->linkRead;
 
-             }
 
-         } elseif (!$this->linkID) {
 
-             // 默认单数据库
 
-             $this->linkID = $this->connect();
 
-         }
 
-     }
 
-     /**
 
-      * 连接分布式服务器
 
-      * @access protected
 
-      * @param boolean $master 主服务器
 
-      * @return PDO
 
-      */
 
-     protected function multiConnect($master = false)
 
-     {
 
-         $_config = [];
 
-         // 分布式数据库配置解析
 
-         foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
 
-             $_config[$name] = explode(',', $this->config[$name]);
 
-         }
 
-         // 主服务器序号
 
-         $m = floor(mt_rand(0, $this->config['master_num'] - 1));
 
-         if ($this->config['rw_separate']) {
 
-             // 主从式采用读写分离
 
-             if ($master) // 主服务器写入
 
-             {
 
-                 $r = $m;
 
-             } elseif (is_numeric($this->config['slave_no'])) {
 
-                 // 指定服务器读
 
-                 $r = $this->config['slave_no'];
 
-             } else {
 
-                 // 读操作连接从服务器 每次随机连接的数据库
 
-                 $r = floor(mt_rand($this->config['master_num'], count($_config['hostname']) - 1));
 
-             }
 
-         } else {
 
-             // 读写操作不区分服务器 每次随机连接的数据库
 
-             $r = floor(mt_rand(0, count($_config['hostname']) - 1));
 
-         }
 
-         $dbMaster = false;
 
-         if ($m != $r) {
 
-             $dbMaster = [];
 
-             foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
 
-                 $dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0];
 
-             }
 
-         }
 
-         $dbConfig = [];
 
-         foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
 
-             $dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0];
 
-         }
 
-         return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster);
 
-     }
 
-     /**
 
-      * 析构方法
 
-      * @access public
 
-      */
 
-     public function __destruct()
 
-     {
 
-         // 释放查询
 
-         if ($this->PDOStatement) {
 
-             $this->free();
 
-         }
 
-         // 关闭连接
 
-         $this->close();
 
-     }
 
- }
 
 
  |