Merge branch 'master' into bugfix3

This commit is contained in:
苗高鹏
2017-02-13 14:53:24 +08:00
committed by GitHub
45 changed files with 1843 additions and 836 deletions

View File

@@ -112,8 +112,8 @@ abstract class Builder
$result[$item] = $val;
} else {
$key = str_replace('.', '_', $key);
$this->query->bind($key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
$result[$item] = ':' . $key;
$this->query->bind('__data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
$result[$item] = ':__data__' . $key;
}
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入
@@ -250,7 +250,10 @@ abstract class Builder
// 使用闭包查询
$query = new Query($this->connection);
call_user_func_array($value, [ & $query]);
$str[] = ' ' . $key . ' ( ' . $this->buildWhere($query->getOptions('where'), $options) . ' )';
$whereClause = $this->buildWhere($query->getOptions('where'), $options);
if (!empty($whereClause)) {
$str[] = ' ' . $key . ' ( ' . $whereClause . ' )';
}
} elseif (strpos($field, '|')) {
// 不同字段使用相同查询条件OR
$array = explode('|', $field);
@@ -523,10 +526,12 @@ abstract class Builder
$array = [];
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
if ('[rand]' == $val) {
$array[] = $this->parseRand();
} elseif (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';

View File

@@ -13,7 +13,6 @@ namespace think\db;
use PDO;
use PDOStatement;
use think\Collection;
use think\Db;
use think\db\exception\BindParamException;
use think\Debug;
@@ -51,8 +50,6 @@ abstract class Connection
protected $linkRead;
protected $linkWrite;
// 查询结果类型
protected $resultSetType = 'array';
// 查询结果类型
protected $fetchType = PDO::FETCH_ASSOC;
// 字段属性大小写
@@ -61,6 +58,8 @@ abstract class Connection
protected static $event = [];
// 查询对象
protected $query = [];
// 使用Builder类
protected $builder;
// 数据库连接参数配置
protected $config = [
// 数据库类型
@@ -109,6 +108,8 @@ abstract class Connection
'builder' => '',
// Query类
'query' => '\\think\\db\\Query',
// 是否需要断线重连
'break_reconnect' => false,
];
// PDO连接参数
@@ -151,6 +152,20 @@ abstract class Connection
return $this->query[$model];
}
/**
* 获取当前连接器类对应的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
@@ -270,10 +285,7 @@ abstract class Connection
}
// 记录当前字段属性大小写设置
$this->attrCase = $params[PDO::ATTR_CASE];
// 记录数据集返回类型
if (isset($config['resultset_type'])) {
$this->resultSetType = $config['resultset_type'];
}
// 数据返回类型
if (isset($config['result_type'])) {
$this->fetchType = $config['result_type'];
@@ -353,8 +365,8 @@ abstract class Connection
$this->bind = $bind;
}
//释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
// 释放前次的查询结果
if (!empty($this->PDOStatement)) {
$this->free();
}
@@ -381,6 +393,9 @@ abstract class Connection
// 返回结果集
return $this->getResult($pdo, $procedure);
} catch (\PDOException $e) {
if ($this->config['break_reconnect'] && $this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw new PDOException($e, $this->config, $this->getLastsql());
}
}
@@ -436,6 +451,9 @@ abstract class Connection
$this->numRows = $this->PDOStatement->rowCount();
return $this->numRows;
} catch (\PDOException $e) {
if ($this->config['break_reconnect'] && $this->isBreak($e)) {
return $this->close()->execute($sql, $bind);
}
throw new PDOException($e, $this->config, $this->getLastsql());
}
}
@@ -511,11 +529,13 @@ abstract class Connection
protected function bindParam($bind)
{
foreach ($bind as $key => $val) {
if (is_numeric($key)) {
$key = $key + 1;
$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);
}
array_unshift($val, $key);
$result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val);
if (!$result) {
$param = array_shift($val);
throw new BindParamException(
@@ -529,11 +549,11 @@ abstract class Connection
}
/**
* 获得数据集
* 获得数据集数组
* @access protected
* @param bool $pdo 是否返回PDOStatement
* @param bool $procedure 是否存储过程
* @return mixed
* @return array
*/
protected function getResult($pdo = false, $procedure = false)
{
@@ -547,11 +567,6 @@ abstract class Connection
}
$result = $this->PDOStatement->fetchAll($this->fetchType);
$this->numRows = count($result);
if ('collection' == $this->resultSetType) {
// 返回数据集Collection对象
$result = new Collection($result);
}
return $result;
}
@@ -740,12 +755,28 @@ abstract class Connection
}
/**
* 关闭数据库
* 关闭数据库(或者重新连接)
* @access public
* @return $this
*/
public function close()
{
$this->linkID = null;
$this->linkID = null;
$this->linkWrite = null;
$this->linkRead = null;
$this->links = [];
return $this;
}
/**
* 是否断线
* @access protected
* @param \PDOException $e 异常
* @return bool
*/
protected function isBreak($e)
{
return false;
}
/**

View File

@@ -124,8 +124,7 @@ class Query
*/
protected function setBuilder()
{
$builder = $this->connection->getConfig('builder') ?: $this->connection->getConfig('type');
$class = false !== strpos($builder, '\\') ? $builder : '\\think\\db\\builder\\' . ucfirst($builder);
$class = $this->connection->getBuilder();
$this->builder = new $class($this->connection, $this);
}
@@ -229,8 +228,8 @@ class Query
/**
* 执行语句
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param string $sql sql指令
* @param array $bind 参数绑定
* @return int
* @throws BindParamException
* @throws PDOException
@@ -243,7 +242,7 @@ class Query
/**
* 获取最近插入的ID
* @access public
* @param string $sequence 自增序列名
* @param string $sequence 自增序列名
* @return string
*/
public function getLastInsID($sequence = null)
@@ -390,7 +389,7 @@ class Query
* @access public
* @param string $field 字段名
* @param mixed $default 默认值
* @param bool $force 强制转为数字类型
* @param bool $force 强制转为数字类型
* @return mixed
*/
public function value($field, $default = null, $force = false)
@@ -409,7 +408,7 @@ class Query
if (isset($this->options['field'])) {
unset($this->options['field']);
}
$pdo = $this->field($field)->fetchPdo(true)->find();
$pdo = $this->field($field)->limit(1)->getPdo();
if (is_string($pdo)) {
// 返回SQL语句
return $pdo;
@@ -420,11 +419,7 @@ class Query
}
if (isset($cache)) {
// 缓存数据
if (isset($cache['tag'])) {
Cache::tag($cache['tag'])->set($key, $result, $cache['expire']);
} else {
Cache::set($key, $result, $cache['expire']);
}
$this->cacheData($key, $result, $cache);
}
} else {
// 清空查询条件
@@ -459,7 +454,7 @@ class Query
if ($key && '*' != $field) {
$field = $key . ',' . $field;
}
$pdo = $this->field($field)->fetchPdo(true)->select();
$pdo = $this->field($field)->getPdo();
if (is_string($pdo)) {
// 返回SQL语句
return $pdo;
@@ -492,11 +487,7 @@ class Query
}
if (isset($cache) && isset($guid)) {
// 缓存数据
if (isset($cache['tag'])) {
Cache::tag($cache['tag'])->set($guid, $result, $cache['expire']);
} else {
Cache::set($guid, $result, $cache['expire']);
}
$this->cacheData($guid, $result, $cache);
}
} else {
// 清空查询条件
@@ -509,10 +500,17 @@ class Query
* COUNT查询
* @access public
* @param string $field 字段名
* @return integer
* @return integer|string
*/
public function count($field = '*')
{
if (isset($this->options['group'])) {
// 支持GROUP
$options = $this->getOptions();
$subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql();
return $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true);
}
return $this->value('COUNT(' . $field . ') AS tp_count', 0, true);
}
@@ -522,7 +520,7 @@ class Query
* @param string $field 字段名
* @return float|int
*/
public function sum($field = '*')
public function sum($field)
{
return $this->value('SUM(' . $field . ') AS tp_sum', 0, true);
}
@@ -533,7 +531,7 @@ class Query
* @param string $field 字段名
* @return mixed
*/
public function min($field = '*')
public function min($field)
{
return $this->value('MIN(' . $field . ') AS tp_min', 0, true);
}
@@ -544,7 +542,7 @@ class Query
* @param string $field 字段名
* @return mixed
*/
public function max($field = '*')
public function max($field)
{
return $this->value('MAX(' . $field . ') AS tp_max', 0, true);
}
@@ -555,7 +553,7 @@ class Query
* @param string $field 字段名
* @return float|int
*/
public function avg($field = '*')
public function avg($field)
{
return $this->value('AVG(' . $field . ') AS tp_avg', 0, true);
}
@@ -694,7 +692,7 @@ class Query
* 获取Join表名及别名 支持
* ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias'
* @access public
* @param array|string $join
* @param array|string $join
* @return array|string
*/
protected function getJoinTable($join, &$alias = null)
@@ -817,8 +815,8 @@ class Query
/**
* 字段值增长
* @access public
* @param string|array $field 字段名
* @param integer $step 增长值
* @param string|array $field 字段名
* @param integer $step 增长值
* @return $this
*/
public function inc($field, $step = 1)
@@ -833,8 +831,8 @@ class Query
/**
* 字段值减少
* @access public
* @param string|array $field 字段名
* @param integer $step 增长值
* @param string|array $field 字段名
* @param integer $step 增长值
* @return $this
*/
public function dec($field, $step = 1)
@@ -849,8 +847,8 @@ class Query
/**
* 使用表达式设置数据
* @access public
* @param string $field 字段名
* @param string $value 字段值
* @param string $field 字段名
* @param string $value 字段值
* @return $this
*/
public function exp($field, $value)
@@ -871,7 +869,7 @@ class Query
public function view($join, $field = true, $on = null, $type = 'INNER')
{
$this->options['view'] = true;
if (is_array($join) && is_null($field)) {
if (is_array($join) && key($join) !== 0) {
foreach ($join as $key => $val) {
$this->view($key, $val[0], isset($val[1]) ? $val[1] : null, isset($val[2]) ? $val[2] : 'INNER');
}
@@ -972,6 +970,156 @@ class Query
return $this;
}
/**
* 指定Null查询条件
* @access public
* @param mixed $field 查询字段
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereNull($field, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'null', null);
return $this;
}
/**
* 指定NotNull查询条件
* @access public
* @param mixed $field 查询字段
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereNotNull($field, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'notnull', null);
return $this;
}
/**
* 指定Exists查询条件
* @access public
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereExists($condition, $logic = 'AND')
{
$this->options['where'][strtoupper($logic)][] = ['exists', $condition];
return $this;
}
/**
* 指定NotExists查询条件
* @access public
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereNotExists($condition, $logic = 'AND')
{
$this->options['where'][strtoupper($logic)][] = ['not exists', $condition];
return $this;
}
/**
* 指定In查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereIn($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'in', $condition);
return $this;
}
/**
* 指定NotIn查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereNotIn($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'not in', $condition);
return $this;
}
/**
* 指定Like查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereLike($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'like', $condition);
return $this;
}
/**
* 指定NotLike查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereNotLike($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'not like', $condition);
return $this;
}
/**
* 指定Between查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereBetween($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'between', $condition);
return $this;
}
/**
* 指定NotBetween查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereNotBetween($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'not between', $condition);
return $this;
}
/**
* 指定Exp查询条件
* @access public
* @param mixed $field 查询字段
* @param mixed $condition 查询条件
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereExp($field, $condition, $logic = 'AND')
{
$this->parseWhereExp($logic, $field, 'exp', $condition);
return $this;
}
/**
* 分析查询表达式
* @access public
@@ -984,6 +1132,7 @@ class Query
*/
protected function parseWhereExp($logic, $field, $op, $condition, $param = [])
{
$logic = strtoupper($logic);
if ($field instanceof \Closure) {
$this->options['where'][$logic][] = is_string($op) ? [$op, $field] : $field;
return;
@@ -1003,20 +1152,25 @@ class Query
// 数组批量查询
$where = $field;
foreach ($where as $k => $val) {
$this->options['multi'][$k][] = $val;
$this->options['multi'][$logic][$k][] = $val;
}
} elseif ($field && is_string($field)) {
// 字符串查询
$where[$field] = ['null', ''];
$where[$field] = ['null', ''];
$this->options['multi'][$logic][$field][] = $where[$field];
}
} elseif (is_array($op)) {
$where[$field] = $param;
} elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) {
// null查询
$where[$field] = [$op, ''];
$where[$field] = [$op, ''];
$this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_null($condition)) {
// 字段相等查询
$where[$field] = ['eq', $op];
if ('AND' != $logic) {
$this->options['multi'][$logic][$field][] = $where[$field];
}
} else {
$where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null];
if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) {
@@ -1024,18 +1178,18 @@ class Query
$this->bind($param[2]);
}
// 记录一个字段多次查询条件
$this->options['multi'][$field][] = $where[$field];
$this->options['multi'][$logic][$field][] = $where[$field];
}
if (!empty($where)) {
if (!isset($this->options['where'][$logic])) {
$this->options['where'][$logic] = [];
}
if (is_string($field) && $this->checkMultiField($field)) {
$where[$field] = $this->options['multi'][$field];
if (is_string($field) && $this->checkMultiField($field, $logic)) {
$where[$field] = $this->options['multi'][$logic][$field];
} elseif (is_array($field)) {
foreach ($field as $key => $val) {
if ($this->checkMultiField($key)) {
$where[$key] = $this->options['multi'][$key];
if ($this->checkMultiField($key, $logic)) {
$where[$key] = $this->options['multi'][$logic][$key];
}
}
}
@@ -1043,17 +1197,23 @@ class Query
}
}
// 检查是否存在一个字段多次查询条件
private function checkMultiField($field)
/**
* 检查是否存在一个字段多次查询条件
* @access public
* @param string $field 查询字段
* @param string $logic 查询逻辑 and or xor
* @return bool
*/
private function checkMultiField($field, $logic)
{
return isset($this->options['multi'][$field]) && count($this->options['multi'][$field]) > 1;
return isset($this->options['multi'][$logic][$field]) && count($this->options['multi'][$logic][$field]) > 1;
}
/**
* 去除某个查询条件
* @access public
* @param string $field 查询字段
* @param string $logic 查询逻辑 and or xor
* @param string $field 查询字段
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function removeWhereField($field, $logic = 'AND')
@@ -1068,7 +1228,7 @@ class Query
/**
* 去除查询参数
* @access public
* @param string|bool $option 参数名 true 表示去除所有参数
* @param string|bool $option 参数名 true 表示去除所有参数
* @return $this
*/
public function removeOption($option = true)
@@ -1115,17 +1275,17 @@ class Query
/**
* 分页查询
* @param int|null $listRows 每页数量
* @param int|bool $simple 简洁模式或者总记录数
* @param array $config 配置参数
* page:当前页,
* path:url路径,
* query:url额外参数,
* fragment:url锚点,
* var_page:分页变量,
* list_rows:每页数量
* type:分页类名
* @return \think\paginator\Collection
* @param int|array $listRows 每页数量 数组表示配置参数
* @param int|bool $simple 是否简洁模式或者总记录数
* @param array $config 配置参数
* page:当前页,
* path:url路径,
* query:url额外参数,
* fragment:url锚点,
* var_page:分页变量,
* list_rows:每页数量
* type:分页类名
* @return \think\Paginator
* @throws DbException
*/
public function paginate($listRows = null, $simple = false, $config = [])
@@ -1134,8 +1294,13 @@ class Query
$total = $simple;
$simple = false;
}
$config = array_merge(Config::get('paginate'), $config);
$listRows = $listRows ?: $config['list_rows'];
if (is_array($listRows)) {
$config = array_merge(Config::get('paginate'), $listRows);
$listRows = $config['list_rows'];
} else {
$config = array_merge(Config::get('paginate'), $config);
$listRows = $listRows ?: $config['list_rows'];
}
/** @var Paginator $class */
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
@@ -1150,9 +1315,9 @@ class Query
if (!isset($total) && !$simple) {
$options = $this->getOptions();
if (isset($options['order'])) {
unset($this->options['order']);
}
unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
$bind = $this->bind;
$total = $this->count();
$results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
@@ -1400,7 +1565,7 @@ class Query
*/
public function fetchPdo($pdo = true)
{
$this->options['fetch_class'] = $pdo;
$this->options['fetch_pdo'] = $pdo;
return $this;
}
@@ -1479,7 +1644,7 @@ class Query
switch (strtolower($op)) {
case 'today':
case 'd':
$range = 'today';
$range = ['today', 'tomorrow'];
break;
case 'week':
case 'w':
@@ -1505,6 +1670,8 @@ class Query
case 'last year':
$range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])];
break;
default:
$range = $op;
}
$op = is_array($range) ? 'between' : '>';
}
@@ -1735,15 +1902,19 @@ class Query
}
}
$this->via();
$this->options['with'] = $with;
if (isset($this->options['with'])) {
$this->options['with'] = array_merge($this->options['with'], $with);
} else {
$this->options['with'] = $with;
}
return $this;
}
/**
* 关联统计
* @access public
* @param string|array $relation 关联方法名
* @param bool $subQuery 是否使用子查询
* @param string|array $relation 关联方法名
* @param bool $subQuery 是否使用子查询
* @return $this
*/
public function withCount($relation, $subQuery = true)
@@ -1800,12 +1971,22 @@ class Query
/**
* 设置关联查询
* @access public
* @param string $relation 关联名称
* @param string|array $relation 关联名称
* @return $this
*/
public function relation($relation)
{
$this->options['relation'] = $relation;
if (empty($relation)) {
return $this;
}
if (is_string($relation)) {
$relation = explode(',', $relation);
}
if (isset($this->options['relation'])) {
$this->options['relation'] = array_mrege($this->options['relation'], $relation);
} else {
$this->options['relation'] = $relation;
}
return $this;
}
@@ -1881,11 +2062,20 @@ class Query
// 执行操作
$result = $this->execute($sql, $bind);
if ($result) {
$sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
$lastInsId = $this->getLastInsID($sequence);
if ($lastInsId) {
$pk = $this->getPk($options);
if (is_string($pk)) {
$data[$pk] = $lastInsId;
}
}
$options['data'] = $data;
$this->trigger('after_insert', $options);
}
if ($getLastInsID) {
$sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
return $this->getLastInsID($sequence);
if ($getLastInsID) {
return $lastInsId;
}
}
return $result;
}
@@ -1968,8 +2158,8 @@ class Query
$options = $this->parseExpress();
$data = array_merge($options['data'], $data);
$pk = $this->getPk($options);
if (isset($options['cache']) && is_string($options['cache'])) {
$key = $options['cache'];
if (isset($options['cache']) && is_string($options['cache']['key'])) {
$key = $options['cache']['key'];
}
if (empty($options['where'])) {
@@ -1998,9 +2188,10 @@ class Query
} else {
$options['where']['AND'] = $where;
}
} elseif (is_string($pk) && isset($options['where']['AND'][$pk]) && is_scalar($options['where']['AND'][$pk])) {
$key = 'think:' . $options['table'] . '|' . $options['where']['AND'][$pk];
} elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) {
$key = $this->getCacheKey($options['where']['AND'][$pk], $options);
}
// 生成UPDATE SQL语句
$sql = $this->builder->update($data, $options);
// 获取参数绑定
@@ -2013,16 +2204,46 @@ class Query
if (isset($key) && Cache::get($key)) {
// 删除缓存
Cache::rm($key);
} elseif (!empty($options['cache']['tag'])) {
Cache::clear($options['cache']['tag']);
}
// 执行操作
$result = '' == $sql ? 0 : $this->execute($sql, $bind);
if ($result) {
if (isset($where[$pk])) {
$data[$pk] = $where[$pk];
} elseif (is_string($pk) && isset($key) && strpos($key, '|')) {
list($a, $val) = explode('|', $key);
$data[$pk] = $val;
}
$options['data'] = $data;
$this->trigger('after_update', $options);
}
return $result;
}
}
/**
* 执行查询但只返回PDOStatement对象
* @access public
* @return \PDOStatement|string
*/
public function getPdo()
{
// 分析查询表达式
$options = $this->parseExpress();
// 生成查询SQL
$sql = $this->builder->select($options);
// 获取参数绑定
$bind = $this->getBind();
if ($options['fetch_sql']) {
// 获取实际执行的SQL语句
return $this->connection->getRealSql($sql, $bind);
}
// 执行查询操作
return $this->query($sql, $bind, $options['master'], true);
}
/**
* 查找记录
* @access public
@@ -2068,10 +2289,12 @@ class Query
// 获取实际执行的SQL语句
return $this->connection->getRealSql($sql, $bind);
}
$options['data'] = $data;
if ($resultSet = $this->trigger('before_select', $options)) {
} else {
// 执行查询操作
$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_class']);
$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']);
if ($resultSet instanceof \PDOStatement) {
// 返回PDOStatement对象
@@ -2081,40 +2304,42 @@ class Query
if (isset($cache)) {
// 缓存数据集
if (isset($cache['tag'])) {
Cache::tag($cache['tag'])->set($key, $resultSet, $cache['expire']);
} else {
Cache::set($key, $resultSet, $cache['expire']);
}
$this->cacheData($key, $resultSet, $cache);
}
}
// 数据列表读取后的处理
if (!empty($this->model)) {
// 生成模型对象
$model = $this->model;
$modelName = $this->model;
if (count($resultSet) > 0) {
foreach ($resultSet as $key => $result) {
/** @var Model $result */
$result = new $model($result);
$result->isUpdate(true);
$model = new $modelName($result);
$model->isUpdate(true);
// 关联查询
if (!empty($options['relation'])) {
$result->relationQuery($options['relation']);
$model->relationQuery($options['relation']);
}
// 关联统计
if (!empty($options['with_count'])) {
$result->relationCount($result, $options['with_count']);
$model->relationCount($model, $options['with_count']);
}
$resultSet[$key] = $result;
$resultSet[$key] = $model;
}
if (!empty($options['with'])) {
// 预载入
$result->eagerlyResultSet($resultSet, $options['with'], is_object($resultSet) ? get_class($resultSet) : '');
$model->eagerlyResultSet($resultSet, $options['with']);
}
// 模型数据集转换
$resultSet = $model->toCollection($resultSet);
} else {
$resultSet = (new $modelName)->toCollection($resultSet);
}
// 模型数据集转换
$resultSet = (new $model)->toCollection($resultSet);
} elseif ('collection' == $this->connection->getConfig('resultset_type')) {
// 返回Collection对象
$resultSet = new Collection($resultSet);
}
// 返回结果处理
if (!empty($options['fail']) && count($resultSet) == 0) {
@@ -2123,6 +2348,42 @@ class Query
return $resultSet;
}
/**
* 缓存数据
* @access public
* @param string $key 缓存标识
* @param mixed $data 缓存数据
* @param array $config 缓存参数
*/
protected function cacheData($key, $data, $config = [])
{
if (isset($config['tag'])) {
Cache::tag($config['tag'])->set($key, $data, $config['expire']);
} else {
Cache::set($key, $data, $config['expire']);
}
}
/**
* 生成缓存标识
* @access public
* @param mixed $value 缓存数据
* @param array $options 缓存参数
*/
protected function getCacheKey($value, $options)
{
if (is_scalar($value)) {
$data = $value;
} elseif (is_array($value) && 'eq' == strtolower($value[0])) {
$data = $value[1];
}
if (isset($data)) {
return 'think:' . $options['table'] . '|' . $data;
} else {
return md5(serialize($options));
}
}
/**
* 查找单条记录
* @access public
@@ -2142,10 +2403,12 @@ class Query
}
// 分析查询表达式
$options = $this->parseExpress();
$pk = $this->getPk($options);
if (!is_null($data)) {
// AR模式分析主键条件
$this->parsePkWhere($data, $options);
} elseif (!empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['AND'][$pk])) {
$key = $this->getCacheKey($options['where']['AND'][$pk], $options);
}
$options['limit'] = 1;
@@ -2155,12 +2418,14 @@ class Query
$cache = $options['cache'];
if (true === $cache['key'] && !is_null($data) && !is_array($data)) {
$key = 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} else {
$key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
} elseif (is_string($cache['key'])) {
$key = $cache['key'];
} elseif (!isset($key)) {
$key = md5(serialize($options));
}
$result = Cache::get($key);
}
if (!$result) {
if (false === $result) {
// 生成查询SQL
$sql = $this->builder->select($options);
// 获取参数绑定
@@ -2169,56 +2434,61 @@ class Query
// 获取实际执行的SQL语句
return $this->connection->getRealSql($sql, $bind);
}
if (is_string($pk)) {
if (!is_array($data)) {
if (isset($key) && strpos($key, '|')) {
list($a, $val) = explode('|', $key);
$item[$pk] = $val;
} else {
$item[$pk] = $data;
}
$data = $item;
}
}
$options['data'] = $data;
// 事件回调
if ($result = $this->trigger('before_find', $options)) {
} else {
// 执行查询
$result = $this->query($sql, $bind, $options['master'], $options['fetch_class']);
$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']);
if ($result instanceof \PDOStatement) {
if ($resultSet instanceof \PDOStatement) {
// 返回PDOStatement对象
return $result;
return $resultSet;
}
$result = isset($resultSet[0]) ? $resultSet[0] : null;
}
if (isset($cache)) {
// 缓存数据
if (isset($cache['tag'])) {
Cache::tag($cache['tag'])->set($key, $result, $cache['expire']);
} else {
Cache::set($key, $result, $cache['expire']);
}
$this->cacheData($key, $result, $cache);
}
}
// 数据处理
if (!empty($result[0])) {
$data = $result[0];
if (!empty($result)) {
if (!empty($this->model)) {
// 返回模型对象
$model = $this->model;
$data = new $model($data);
$data->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null);
$model = $this->model;
$result = new $model($result);
$result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null);
// 关联查询
if (!empty($options['relation'])) {
$data->relationQuery($options['relation']);
$result->relationQuery($options['relation']);
}
// 预载入查询
if (!empty($options['with'])) {
$data->eagerlyResult($data, $options['with'], is_object($result) ? get_class($result) : '');
$result->eagerlyResult($result, $options['with']);
}
// 关联统计
if (!empty($options['with_count'])) {
$data->relationCount($data, $options['with_count']);
$result->relationCount($result, $options['with_count']);
}
}
} elseif (!empty($options['fail'])) {
$this->throwNotFound($options);
} else {
$data = null;
}
return $data;
return $result;
}
/**
@@ -2349,8 +2619,9 @@ class Query
{
// 分析查询表达式
$options = $this->parseExpress();
if (isset($options['cache']) && is_string($options['cache'])) {
$key = $options['cache'];
$pk = $this->getPk($options);
if (isset($options['cache']) && is_string($options['cache']['key'])) {
$key = $options['cache']['key'];
}
if (!is_null($data) && true !== $data) {
@@ -2360,6 +2631,8 @@ class Query
}
// AR模式分析主键条件
$this->parsePkWhere($data, $options);
} elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) {
$key = $this->getCacheKey($options['where']['AND'][$pk], $options);
}
if (true !== $data && empty($options['where'])) {
@@ -2379,10 +2652,18 @@ class Query
if (isset($key) && Cache::get($key)) {
// 删除缓存
Cache::rm($key);
} elseif (!empty($options['cache']['tag'])) {
Cache::clear($options['cache']['tag']);
}
// 执行操作
$result = $this->execute($sql, $bind);
if ($result) {
if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) {
list($a, $val) = explode('|', $key);
$item[$pk] = $val;
$data = $item;
}
$options['data'] = $data;
$this->trigger('after_delete', $options);
}
return $result;
@@ -2454,7 +2735,7 @@ class Query
$options['strict'] = $this->getConfig('fields_strict');
}
foreach (['master', 'lock', 'fetch_class', 'fetch_sql', 'distinct'] as $name) {
foreach (['master', 'lock', 'fetch_pdo', 'fetch_sql', 'distinct'] as $name) {
if (!isset($options[$name])) {
$options[$name] = false;
}
@@ -2482,8 +2763,8 @@ class Query
/**
* 注册回调方法
* @access public
* @param string $event 事件名
* @param callable $callback 回调方法
* @param string $event 事件名
* @param callable $callback 回调方法
* @return void
*/
public static function event($event, $callback)
@@ -2494,16 +2775,16 @@ class Query
/**
* 触发事件
* @access protected
* @param string $event 事件名
* @param mixed $options 当前查询参数
* @param string $event 事件名
* @param mixed $params 额外参数
* @return bool
*/
protected function trigger($event, $options = [])
protected function trigger($event, $params = [])
{
$result = false;
if (isset(self::$event[$event])) {
$callback = self::$event[$event];
$result = call_user_func_array($callback, [$options, $this]);
$result = call_user_func_array($callback, [$params, $this]);
}
return $result;
}

View File

@@ -38,6 +38,8 @@ class Mysql extends Builder
list($table, $key) = explode('.', $key, 2);
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
} elseif ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
}
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {

View File

@@ -57,6 +57,8 @@ class Pgsql extends Builder
list($table, $key) = explode('.', $key, 2);
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
} elseif ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
}
if (isset($table)) {

View File

@@ -62,6 +62,8 @@ class Sqlite extends Builder
list($table, $key) = explode('.', $key, 2);
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
} elseif ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
}
if (isset($table)) {

View File

@@ -75,6 +75,8 @@ class Sqlsrv extends Builder
list($table, $key) = explode('.', $key, 2);
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
} elseif ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
}
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {

View File

@@ -21,6 +21,8 @@ use think\Log;
class Mysql extends Connection
{
protected $builder = '\\think\\db\\builder\\Mysql';
/**
* 解析pdo连接的dsn信息
* @access protected
@@ -127,4 +129,18 @@ class Mysql extends Connection
{
return true;
}
/**
* 是否断线
* @access protected
* @param \PDOException $e 异常对象
* @return bool
*/
protected function isBreak($e)
{
if (false !== stripos($e->getMessage(), 'server has gone away')) {
return true;
}
return false;
}
}

View File

@@ -19,6 +19,7 @@ use think\db\Connection;
*/
class Pgsql extends Connection
{
protected $builder = '\\think\\db\\builder\\Pgsql';
/**
* 解析pdo连接的dsn信息

View File

@@ -20,6 +20,8 @@ use think\db\Connection;
class Sqlite extends Connection
{
protected $builder = '\\think\\db\\builder\\Sqlite';
/**
* 解析pdo连接的dsn信息
* @access protected

View File

@@ -25,7 +25,7 @@ class Sqlsrv extends Connection
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_STRINGIFY_FETCHES => false,
];
protected $builder = '\\think\\db\\builder\\Sqlsrv';
/**
* 解析pdo连接的dsn信息
* @access protected