tp db更新

This commit is contained in:
sugar1569 2018-12-22 12:55:30 +08:00
parent 2b351f8b15
commit f37507e33b
14 changed files with 576 additions and 230 deletions

View File

@ -115,16 +115,18 @@ abstract class Builder
} elseif (is_array($val) && !empty($val)) { } elseif (is_array($val) && !empty($val)) {
switch (strtolower($val[0])) { switch (strtolower($val[0])) {
case 'inc': case 'inc':
//$result[$item] = $item . '+' . floatval($val[1]); // $result[$item] = $item . '+' . floatval($val[1]);
if ($key == $val[1]) { if ($key == $val[1]) {
$result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]); $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
} }
break; break;
case 'dec': case 'dec':
//$result[$item] = $item . '-' . floatval($val[1]); // $result[$item] = $item . '-' . floatval($val[1]);
if ($key == $val[1]) { if ($key == $val[1]) {
$result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]); $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
} }
break; break;
case 'exp': case 'exp':
throw new Exception('not support data:[' . $val[0] . ']'); throw new Exception('not support data:[' . $val[0] . ']');

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -90,6 +90,8 @@ abstract class Connection
'master_num' => 1, 'master_num' => 1,
// 指定从服务器序号 // 指定从服务器序号
'slave_no' => '', 'slave_no' => '',
// 模型写入后自动读取主服务器
'read_master' => false,
// 是否严格检查字段是否存在 // 是否严格检查字段是否存在
'fields_strict' => true, 'fields_strict' => true,
// 数据返回类型 // 数据返回类型
@ -354,15 +356,15 @@ abstract class Connection
$this->bind = $bind; $this->bind = $bind;
} }
// 释放前次的查询结果
if (!empty($this->PDOStatement)) {
$this->free();
}
Db::$queryTimes++; Db::$queryTimes++;
try { try {
// 调试开始 // 调试开始
$this->debug(true); $this->debug(true);
// 释放前次的查询结果
if (!empty($this->PDOStatement)) {
$this->free();
}
// 预处理 // 预处理
if (empty($this->PDOStatement)) { if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
@ -378,7 +380,7 @@ abstract class Connection
// 执行查询 // 执行查询
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false, '', $master);
// 返回结果集 // 返回结果集
return $this->getResult($pdo, $procedure); return $this->getResult($pdo, $procedure);
} catch (\PDOException $e) { } catch (\PDOException $e) {
@ -386,6 +388,11 @@ abstract class Connection
return $this->close()->query($sql, $bind, $master, $pdo); return $this->close()->query($sql, $bind, $master, $pdo);
} }
throw new PDOException($e, $this->config, $this->getLastsql()); 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) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo); return $this->close()->query($sql, $bind, $master, $pdo);
@ -397,13 +404,14 @@ abstract class Connection
/** /**
* 执行语句 * 执行语句
* @access public * @access public
* @param string $sql sql指令 * @param string $sql sql指令
* @param array $bind 参数绑定 * @param array $bind 参数绑定
* @param Query $query 查询对象
* @return int * @return int
* @throws PDOException * @throws PDOException
* @throws \Exception * @throws \Exception
*/ */
public function execute($sql, $bind = []) public function execute($sql, $bind = [], Query $query = null)
{ {
$this->initConnect(true); $this->initConnect(true);
if (!$this->linkID) { if (!$this->linkID) {
@ -416,15 +424,15 @@ abstract class Connection
$this->bind = $bind; $this->bind = $bind;
} }
//释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
$this->free();
}
Db::$executeTimes++; Db::$executeTimes++;
try { try {
// 调试开始 // 调试开始
$this->debug(true); $this->debug(true);
//释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
$this->free();
}
// 预处理 // 预处理
if (empty($this->PDOStatement)) { if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
@ -440,18 +448,27 @@ abstract class Connection
// 执行语句 // 执行语句
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false, '', true);
if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) {
$query->readMaster();
}
$this->numRows = $this->PDOStatement->rowCount(); $this->numRows = $this->PDOStatement->rowCount();
return $this->numRows; return $this->numRows;
} catch (\PDOException $e) { } catch (\PDOException $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw new PDOException($e, $this->config, $this->getLastsql()); 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) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw $e; throw $e;
} }
@ -466,6 +483,10 @@ abstract class Connection
*/ */
public function getRealSql($sql, array $bind = []) public function getRealSql($sql, array $bind = [])
{ {
if (is_array($sql)) {
$sql = implode(';', $sql);
}
foreach ($bind as $key => $val) { foreach ($bind as $key => $val) {
$value = is_array($val) ? $val[0] : $val; $value = is_array($val) ? $val[0] : $val;
$type = is_array($val) ? $val[1] : PDO::PARAM_STR; $type = is_array($val) ? $val[1] : PDO::PARAM_STR;
@ -478,8 +499,8 @@ abstract class Connection
$sql = is_numeric($key) ? $sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) : substr_replace($sql, $value, strpos($sql, '?'), 1) :
str_replace( str_replace(
[':' . $key . ')', ':' . $key . ',', ':' . $key . ' '], [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL],
[$value . ')', $value . ',', $value . ' '], [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL],
$sql . ' '); $sql . ' ');
} }
return rtrim($sql); return rtrim($sql);
@ -648,6 +669,11 @@ abstract class Connection
return $this->close()->startTrans(); return $this->close()->startTrans();
} }
throw $e; throw $e;
} catch (\Error $e) {
if ($this->isBreak($e)) {
return $this->close()->startTrans();
}
throw $e;
} }
} }
@ -725,7 +751,7 @@ abstract class Connection
* @param array $sqlArray SQL批处理指令 * @param array $sqlArray SQL批处理指令
* @return boolean * @return boolean
*/ */
public function batchQuery($sqlArray = []) public function batchQuery($sqlArray = [], $bind = [], Query $query = null)
{ {
if (!is_array($sqlArray)) { if (!is_array($sqlArray)) {
return false; return false;
@ -734,7 +760,7 @@ abstract class Connection
$this->startTrans(); $this->startTrans();
try { try {
foreach ($sqlArray as $sql) { foreach ($sqlArray as $sql) {
$this->execute($sql); $this->execute($sql, $bind, $query);
} }
// 提交事务 // 提交事务
$this->commit(); $this->commit();
@ -742,6 +768,7 @@ abstract class Connection
$this->rollback(); $this->rollback();
throw $e; throw $e;
} }
return true; return true;
} }
@ -803,6 +830,7 @@ abstract class Connection
'SSL connection has been closed unexpectedly', 'SSL connection has been closed unexpectedly',
'Error writing data to the connection', 'Error writing data to the connection',
'Resource deadlock avoided', 'Resource deadlock avoided',
'failed with errno',
]; ];
$error = $e->getMessage(); $error = $e->getMessage();
@ -883,9 +911,10 @@ abstract class Connection
* @access protected * @access protected
* @param boolean $start 调试开始标记 true 开始 false 结束 * @param boolean $start 调试开始标记 true 开始 false 结束
* @param string $sql 执行的SQL语句 留空自动获取 * @param string $sql 执行的SQL语句 留空自动获取
* @param boolean $master 主从标记
* @return void * @return void
*/ */
protected function debug($start, $sql = '') protected function debug($start, $sql = '', $master = false)
{ {
if (!empty($this->config['debug'])) { if (!empty($this->config['debug'])) {
// 开启数据库调试模式 // 开启数据库调试模式
@ -902,7 +931,7 @@ abstract class Connection
$result = $this->getExplain($sql); $result = $this->getExplain($sql);
} }
// SQL监听 // SQL监听
$this->trigger($sql, $runtime, $result); $this->trigger($sql, $runtime, $result, $master);
} }
} }
} }
@ -924,19 +953,27 @@ abstract class Connection
* @param string $sql SQL语句 * @param string $sql SQL语句
* @param float $runtime SQL运行时间 * @param float $runtime SQL运行时间
* @param mixed $explain SQL分析 * @param mixed $explain SQL分析
* @return bool * @param bool $master 主从标记
* @return void
*/ */
protected function trigger($sql, $runtime, $explain = []) protected function trigger($sql, $runtime, $explain = [], $master = false)
{ {
if (!empty(self::$event)) { if (!empty(self::$event)) {
foreach (self::$event as $callback) { foreach (self::$event as $callback) {
if (is_callable($callback)) { if (is_callable($callback)) {
call_user_func_array($callback, [$sql, $runtime, $explain]); call_user_func_array($callback, [$sql, $runtime, $explain, $master]);
} }
} }
} else { } else {
// 未注册监听则记录到日志中 // 未注册监听则记录到日志中
Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql'); if ($this->config['deploy']) {
// 分布式记录当前操作的主从
$master = $master ? 'master|' : 'slave|';
} else {
$master = '';
}
Log::record('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]', 'sql');
if (!empty($explain)) { if (!empty($explain)) {
Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql');
} }

View File

@ -0,0 +1,48 @@
<?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;
class Expression
{
/**
* 查询表达式
*
* @var string
*/
protected $value;
/**
* 创建一个查询表达式
*
* @param string $value
* @return void
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* 获取表达式
*
* @return string
*/
public function getValue()
{
return $this->value;
}
public function __toString()
{
return (string) $this->value;
}
}

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -53,14 +53,16 @@ class Query
protected static $info = []; protected static $info = [];
// 回调事件 // 回调事件
private static $event = []; private static $event = [];
// 读取主库
private static $readMaster = [];
/** /**
* 构造函数 * 构造函数
* @access public * @access public
* @param Connection $connection 数据库对象实例 * @param Connection $connection 数据库对象实例
* @param string $model 模型名 * @param Model $model 模型对象
*/ */
public function __construct(Connection $connection = null, $model = '') public function __construct(Connection $connection = null, $model = null)
{ {
$this->connection = $connection ?: Db::connect([], true); $this->connection = $connection ?: Db::connect([], true);
$this->prefix = $this->connection->getConfig('prefix'); $this->prefix = $this->connection->getConfig('prefix');
@ -131,15 +133,34 @@ class Query
} }
/** /**
* 获取当前的模型对象 * 获取当前的模型对象实例
* @access public * @access public
* @return string * @return Model|null
*/ */
public function getModel() public function getModel()
{ {
return $this->model; return $this->model;
} }
/**
* 设置后续从主库读取数据
* @access public
* @param bool $allTable
* @return void
*/
public function readMaster($allTable = false)
{
if ($allTable) {
$table = '*';
} else {
$table = isset($this->options['table']) ? $this->options['table'] : $this->getTable();
}
static::$readMaster[$table] = true;
return $this;
}
/** /**
* 获取当前的builder实例对象 * 获取当前的builder实例对象
* @access public * @access public
@ -238,7 +259,7 @@ class Query
*/ */
public function execute($sql, $bind = []) public function execute($sql, $bind = [])
{ {
return $this->connection->execute($sql, $bind); return $this->connection->execute($sql, $bind, $this);
} }
/** /**
@ -312,9 +333,9 @@ class Query
* @param array $sql SQL批处理指令 * @param array $sql SQL批处理指令
* @return boolean * @return boolean
*/ */
public function batchQuery($sql = []) public function batchQuery($sql = [], $bind = [])
{ {
return $this->connection->batchQuery($sql); return $this->connection->batchQuery($sql, $bind);
} }
/** /**
@ -403,7 +424,7 @@ class Query
if (empty($this->options['table'])) { if (empty($this->options['table'])) {
$this->options['table'] = $this->getTable(); $this->options['table'] = $this->getTable();
} }
$key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind));
$result = Cache::get($key); $result = Cache::get($key);
} }
if (false === $result) { if (false === $result) {
@ -420,7 +441,7 @@ class Query
$result += 0; $result += 0;
} }
if (isset($cache)) { if (isset($cache) && false !== $result) {
// 缓存数据 // 缓存数据
$this->cacheData($key, $result, $cache); $this->cacheData($key, $result, $cache);
} }
@ -447,7 +468,7 @@ class Query
if (empty($this->options['table'])) { if (empty($this->options['table'])) {
$this->options['table'] = $this->getTable(); $this->options['table'] = $this->getTable();
} }
$guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); $guid = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind));
$result = Cache::get($guid); $result = Cache::get($guid);
} }
if (false === $result) { if (false === $result) {
@ -534,22 +555,24 @@ class Query
* MIN查询 * MIN查询
* @access public * @access public
* @param string $field 字段名 * @param string $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed * @return mixed
*/ */
public function min($field) public function min($field, $force = true)
{ {
return $this->value('MIN(' . $field . ') AS tp_min', 0, true); return $this->value('MIN(' . $field . ') AS tp_min', 0, $force);
} }
/** /**
* MAX查询 * MAX查询
* @access public * @access public
* @param string $field 字段名 * @param string $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed * @return mixed
*/ */
public function max($field) public function max($field, $force = true)
{ {
return $this->value('MAX(' . $field . ') AS tp_max', 0, true); return $this->value('MAX(' . $field . ') AS tp_max', 0, $force);
} }
/** /**
@ -607,7 +630,7 @@ class Query
return true; return true;
} }
} }
return $this->setField($field, ['exp', $field . '+' . $step]); return $this->setField($field, ['inc', $step]);
} }
/** /**
@ -635,8 +658,9 @@ class Query
$this->options = []; $this->options = [];
return true; return true;
} }
return $this->setField($field, ['inc', $step]);
} }
return $this->setField($field, ['exp', $field . '-' . $step]); return $this->setField($field, ['dec', $step]);
} }
/** /**
@ -704,7 +728,8 @@ class Query
{ {
// 传入的表名为数组 // 传入的表名为数组
if (is_array($join)) { if (is_array($join)) {
list($table, $alias) = each($join); $table = $join;
$alias = array_shift($join);
} else { } else {
$join = trim($join); $join = trim($join);
if (false !== strpos($join, '(')) { if (false !== strpos($join, '(')) {
@ -725,13 +750,9 @@ class Query
$table = $this->getTable($table); $table = $this->getTable($table);
} }
} }
} if (isset($alias) && $table != $alias) {
if (isset($alias)) { $table = [$table => $alias];
if (isset($this->options['alias'][$table])) {
$table = $table . '@think' . uniqid();
} }
$table = [$table => $alias];
$this->alias($table);
} }
return $table; return $table;
} }
@ -769,8 +790,15 @@ class Query
{ {
if (empty($field)) { if (empty($field)) {
return $this; return $this;
} elseif ($field instanceof Expression) {
$this->options['field'][] = $field;
return $this;
} }
if (is_string($field)) { if (is_string($field)) {
if (preg_match('/[\<\'\"\(]/', $field)) {
return $this->fieldRaw($field);
}
$field = array_map('trim', explode(',', $field)); $field = array_map('trim', explode(',', $field));
} }
if (true === $field) { if (true === $field) {
@ -794,12 +822,30 @@ class Query
} }
if (isset($this->options['field'])) { if (isset($this->options['field'])) {
$field = array_merge($this->options['field'], $field); $field = array_merge((array) $this->options['field'], $field);
} }
$this->options['field'] = array_unique($field); $this->options['field'] = array_unique($field);
return $this; return $this;
} }
/**
* 表达式方式指定查询字段
* @access public
* @param string $field 字段名
* @param array $bind 参数绑定
* @return $this
*/
public function fieldRaw($field, array $bind = [])
{
$this->options['field'][] = $this->raw($field);
if ($bind) {
$this->bind($bind);
}
return $this;
}
/** /**
* 设置数据 * 设置数据
* @access public * @access public
@ -828,7 +874,7 @@ class Query
{ {
$fields = is_string($field) ? explode(',', $field) : $field; $fields = is_string($field) ? explode(',', $field) : $field;
foreach ($fields as $field) { foreach ($fields as $field) {
$this->data($field, ['exp', $field . '+' . $step]); $this->data($field, ['inc', $step]);
} }
return $this; return $this;
} }
@ -844,7 +890,7 @@ class Query
{ {
$fields = is_string($field) ? explode(',', $field) : $field; $fields = is_string($field) ? explode(',', $field) : $field;
foreach ($fields as $field) { foreach ($fields as $field) {
$this->data($field, ['exp', $field . '-' . $step]); $this->data($field, ['dec', $step]);
} }
return $this; return $this;
} }
@ -858,25 +904,36 @@ class Query
*/ */
public function exp($field, $value) public function exp($field, $value)
{ {
$this->data($field, ['exp', $value]); $this->data($field, $this->raw($value));
return $this; return $this;
} }
/**
* 使用表达式设置数据
* @access public
* @param mixed $value 表达式
* @return Expression
*/
public function raw($value)
{
return new Expression($value);
}
/** /**
* 指定JOIN查询字段 * 指定JOIN查询字段
* @access public * @access public
* @param string|array $table 数据表 * @param string|array $table 数据表
* @param string|array $field 查询字段 * @param string|array $field 查询字段
* @param string|array $on JOIN条件 * @param mixed $on JOIN条件
* @param string $type JOIN类型 * @param string $type JOIN类型
* @return $this * @return $this
*/ */
public function view($join, $field = true, $on = null, $type = 'INNER') public function view($join, $field = true, $on = null, $type = 'INNER')
{ {
$this->options['view'] = true; $this->options['view'] = true;
if (is_array($join) && key($join) !== 0) { if (is_array($join) && key($join) === 0) {
foreach ($join as $key => $val) { foreach ($join as $key => $val) {
$this->view($key, $val[0], isset($val[1]) ? $val[1] : null, isset($val[2]) ? $val[2] : 'INNER'); $this->view($val[0], $val[1], isset($val[2]) ? $val[2] : null, isset($val[3]) ? $val[3] : 'INNER');
} }
} else { } else {
$fields = []; $fields = [];
@ -975,6 +1032,37 @@ class Query
return $this; return $this;
} }
/**
* 指定表达式查询条件
* @access public
* @param string $where 查询条件
* @param array $bind 参数绑定
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereRaw($where, $bind = [], $logic = 'AND')
{
$this->options['where'][$logic][] = $this->raw($where);
if ($bind) {
$this->bind($bind);
}
return $this;
}
/**
* 指定表达式查询条件 OR
* @access public
* @param string $where 查询条件
* @param array $bind 参数绑定
* @return $this
*/
public function whereOrRaw($where, $bind = [])
{
return $this->whereRaw($where, $bind, 'OR');
}
/** /**
* 指定Null查询条件 * 指定Null查询条件
* @access public * @access public
@ -984,7 +1072,7 @@ class Query
*/ */
public function whereNull($field, $logic = 'AND') public function whereNull($field, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'null', null); $this->parseWhereExp($logic, $field, 'null', null, [], true);
return $this; return $this;
} }
@ -997,7 +1085,7 @@ class Query
*/ */
public function whereNotNull($field, $logic = 'AND') public function whereNotNull($field, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'notnull', null); $this->parseWhereExp($logic, $field, 'notnull', null, [], true);
return $this; return $this;
} }
@ -1037,7 +1125,7 @@ class Query
*/ */
public function whereIn($field, $condition, $logic = 'AND') public function whereIn($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'in', $condition); $this->parseWhereExp($logic, $field, 'in', $condition, [], true);
return $this; return $this;
} }
@ -1051,7 +1139,7 @@ class Query
*/ */
public function whereNotIn($field, $condition, $logic = 'AND') public function whereNotIn($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'not in', $condition); $this->parseWhereExp($logic, $field, 'not in', $condition, [], true);
return $this; return $this;
} }
@ -1065,7 +1153,7 @@ class Query
*/ */
public function whereLike($field, $condition, $logic = 'AND') public function whereLike($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'like', $condition); $this->parseWhereExp($logic, $field, 'like', $condition, [], true);
return $this; return $this;
} }
@ -1079,7 +1167,7 @@ class Query
*/ */
public function whereNotLike($field, $condition, $logic = 'AND') public function whereNotLike($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'not like', $condition); $this->parseWhereExp($logic, $field, 'not like', $condition, [], true);
return $this; return $this;
} }
@ -1093,7 +1181,7 @@ class Query
*/ */
public function whereBetween($field, $condition, $logic = 'AND') public function whereBetween($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'between', $condition); $this->parseWhereExp($logic, $field, 'between', $condition, [], true);
return $this; return $this;
} }
@ -1107,7 +1195,7 @@ class Query
*/ */
public function whereNotBetween($field, $condition, $logic = 'AND') public function whereNotBetween($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'not between', $condition); $this->parseWhereExp($logic, $field, 'not between', $condition, [], true);
return $this; return $this;
} }
@ -1121,7 +1209,7 @@ class Query
*/ */
public function whereExp($field, $condition, $logic = 'AND') public function whereExp($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'exp', $condition); $this->parseWhereExp($logic, $field, 'exp', $this->raw($condition), [], true);
return $this; return $this;
} }
@ -1148,9 +1236,10 @@ class Query
* @param mixed $op 查询表达式 * @param mixed $op 查询表达式
* @param mixed $condition 查询条件 * @param mixed $condition 查询条件
* @param array $param 查询参数 * @param array $param 查询参数
* @param bool $strict 严格模式
* @return void * @return void
*/ */
protected function parseWhereExp($logic, $field, $op, $condition, $param = []) protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false)
{ {
$logic = strtoupper($logic); $logic = strtoupper($logic);
if ($field instanceof \Closure) { if ($field instanceof \Closure) {
@ -1161,8 +1250,17 @@ class Query
if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) {
$field = $this->options['via'] . '.' . $field; $field = $this->options['via'] . '.' . $field;
} }
if (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {
$where[] = ['exp', $field]; if ($field instanceof Expression) {
return $this->whereRaw($field, is_array($op) ? $op : []);
} elseif ($strict) {
// 使用严格模式查询
$where[$field] = [$op, $condition];
// 记录一个字段多次查询条件
$this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {
$where[] = ['exp', $this->raw($field)];
if (is_array($op)) { if (is_array($op)) {
// 参数绑定 // 参数绑定
$this->bind($op); $this->bind($op);
@ -1183,21 +1281,28 @@ class Query
$where[$field] = $param; $where[$field] = $param;
} elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) {
// null查询 // null查询
$where[$field] = [$op, '']; $where[$field] = [$op, ''];
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_null($condition)) { } elseif (is_null($condition)) {
// 字段相等查询 // 字段相等查询
$where[$field] = ['eq', $op]; $where[$field] = ['eq', $op];
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} else { } else {
$where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; if ('exp' == strtolower($op)) {
if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { $where[$field] = ['exp', $this->raw($condition)];
// 参数绑定 // 参数绑定
$this->bind($param[2]); if (isset($param[2]) && is_array($param[2])) {
$this->bind($param[2]);
}
} else {
$where[$field] = [$op, $condition];
} }
// 记录一个字段多次查询条件 // 记录一个字段多次查询条件
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} }
if (!empty($where)) { if (!empty($where)) {
if (!isset($this->options['where'][$logic])) { if (!isset($this->options['where'][$logic])) {
$this->options['where'][$logic] = []; $this->options['where'][$logic] = [];
@ -1239,6 +1344,7 @@ class Query
$logic = strtoupper($logic); $logic = strtoupper($logic);
if (isset($this->options['where'][$logic][$field])) { if (isset($this->options['where'][$logic][$field])) {
unset($this->options['where'][$logic][$field]); unset($this->options['where'][$logic][$field]);
unset($this->options['multi'][$logic][$field]);
} }
return $this; return $this;
} }
@ -1414,31 +1520,59 @@ class Query
*/ */
public function order($field, $order = null) public function order($field, $order = null)
{ {
if (!empty($field)) { if (empty($field)) {
if (is_string($field)) { return $this;
if (!empty($this->options['via'])) { } elseif ($field instanceof Expression) {
$field = $this->options['via'] . '.' . $field; $this->options['order'][] = $field;
} return $this;
$field = empty($order) ? $field : [$field => $order]; }
} elseif (!empty($this->options['via'])) {
foreach ($field as $key => $val) { if (is_string($field)) {
if (is_numeric($key)) { if (!empty($this->options['via'])) {
$field[$key] = $this->options['via'] . '.' . $val; $field = $this->options['via'] . '.' . $field;
} else {
$field[$this->options['via'] . '.' . $key] = $val;
unset($field[$key]);
}
}
} }
if (!isset($this->options['order'])) { if (strpos($field, ',')) {
$this->options['order'] = []; $field = array_map('trim', explode(',', $field));
}
if (is_array($field)) {
$this->options['order'] = array_merge($this->options['order'], $field);
} else { } else {
$this->options['order'][] = $field; $field = empty($order) ? $field : [$field => $order];
}
} elseif (!empty($this->options['via'])) {
foreach ($field as $key => $val) {
if (is_numeric($key)) {
$field[$key] = $this->options['via'] . '.' . $val;
} else {
$field[$this->options['via'] . '.' . $key] = $val;
unset($field[$key]);
}
} }
} }
if (!isset($this->options['order'])) {
$this->options['order'] = [];
}
if (is_array($field)) {
$this->options['order'] = array_merge($this->options['order'], $field);
} else {
$this->options['order'][] = $field;
}
return $this;
}
/**
* 表达式方式指定Field排序
* @access public
* @param string $field 排序字段
* @param array $bind 参数绑定
* @return $this
*/
public function orderRaw($field, array $bind = [])
{
$this->options['order'][] = $this->raw($field);
if ($bind) {
$this->bind($bind);
}
return $this; return $this;
} }
@ -1523,7 +1657,12 @@ class Query
{ {
if (is_array($alias)) { if (is_array($alias)) {
foreach ($alias as $key => $val) { foreach ($alias as $key => $val) {
$this->options['alias'][$key] = $val; if (false !== strpos($key, '__')) {
$table = $this->parseSqlTable($key);
} else {
$table = $key;
}
$this->options['alias'][$table] = $val;
} }
} else { } else {
if (isset($this->options['table'])) { if (isset($this->options['table'])) {
@ -1651,46 +1790,49 @@ class Query
* 查询日期或者时间 * 查询日期或者时间
* @access public * @access public
* @param string $field 日期字段名 * @param string $field 日期字段名
* @param string $op 比较运算符或者表达式 * @param string|array $op 比较运算符或者表达式
* @param string|array $range 比较范围 * @param string|array $range 比较范围
* @return $this * @return $this
*/ */
public function whereTime($field, $op, $range = null) public function whereTime($field, $op, $range = null)
{ {
if (is_null($range)) { if (is_null($range)) {
// 使用日期表达式 if (is_array($op)) {
$date = getdate(); $range = $op;
switch (strtolower($op)) { } else {
case 'today': // 使用日期表达式
case 'd': switch (strtolower($op)) {
$range = ['today', 'tomorrow']; case 'today':
break; case 'd':
case 'week': $range = ['today', 'tomorrow'];
case 'w': break;
$range = 'this week 00:00:00'; case 'week':
break; case 'w':
case 'month': $range = ['this week 00:00:00', 'next week 00:00:00'];
case 'm': break;
$range = mktime(0, 0, 0, $date['mon'], 1, $date['year']); case 'month':
break; case 'm':
case 'year': $range = ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'];
case 'y': break;
$range = mktime(0, 0, 0, 1, 1, $date['year']); case 'year':
break; case 'y':
case 'yesterday': $range = ['this year 1/1', 'next year 1/1'];
$range = ['yesterday', 'today']; break;
break; case 'yesterday':
case 'last week': $range = ['yesterday', 'today'];
$range = ['last week 00:00:00', 'this week 00:00:00']; break;
break; case 'last week':
case 'last month': $range = ['last week 00:00:00', 'this week 00:00:00'];
$range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])]; break;
break; case 'last month':
case 'last year': $range = ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'];
$range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])]; break;
break; case 'last year':
default: $range = ['last year 1/1', 'this year 1/1'];
$range = $op; break;
default:
$range = $op;
}
} }
$op = is_array($range) ? 'between' : '>'; $op = is_array($range) ? 'between' : '>';
} }
@ -1735,7 +1877,7 @@ class Query
$schema = $guid; $schema = $guid;
} }
// 读取缓存 // 读取缓存
if (is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) { if (!App::$debug && is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) {
$info = include RUNTIME_PATH . 'schema/' . $schema . '.php'; $info = include RUNTIME_PATH . 'schema/' . $schema . '.php';
} else { } else {
$info = $this->connection->getFields($guid); $info = $this->connection->getFields($guid);
@ -1810,7 +1952,9 @@ class Query
*/ */
protected function getFieldBindType($type) protected function getFieldBindType($type)
{ {
if (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
$bind = PDO::PARAM_STR;
} elseif (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) {
$bind = PDO::PARAM_INT; $bind = PDO::PARAM_INT;
} elseif (preg_match('/bool/is', $type)) { } elseif (preg_match('/bool/is', $type)) {
$bind = PDO::PARAM_BOOL; $bind = PDO::PARAM_BOOL;
@ -1892,11 +2036,10 @@ class Query
$with = explode(',', $with); $with = explode(',', $with);
} }
$first = true; $first = true;
$currentModel = $this->model;
/** @var Model $class */ /** @var Model $class */
$class = new $currentModel; $class = $this->model;
foreach ($with as $key => $relation) { foreach ($with as $key => $relation) {
$subRelation = ''; $subRelation = '';
$closure = false; $closure = false;
@ -1955,7 +2098,7 @@ class Query
$relation = $key; $relation = $key;
} }
$relation = Loader::parseName($relation, 1, false); $relation = Loader::parseName($relation, 1, false);
$count = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')'; $count = '(' . $this->model->$relation()->getRelationCountQuery($closure) . ')';
$this->field([$count => Loader::parseName($relation) . '_count']); $this->field([$count => Loader::parseName($relation) . '_count']);
} }
} }
@ -2082,7 +2225,7 @@ class Query
} }
// 执行操作 // 执行操作
$result = 0 === $sql ? 0 : $this->execute($sql, $bind); $result = 0 === $sql ? 0 : $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
$sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
$lastInsId = $this->getLastInsID($sequence); $lastInsId = $this->getLastInsID($sequence);
@ -2118,27 +2261,40 @@ class Query
/** /**
* 批量插入记录 * 批量插入记录
* @access public * @access public
* @param mixed $dataSet 数据集 * @param mixed $dataSet 数据集
* @param boolean $replace 是否replace * @param boolean $replace 是否replace
* @param integer $limit 每次写入数据限制
* @return integer|string * @return integer|string
*/ */
public function insertAll(array $dataSet, $replace = false) public function insertAll(array $dataSet, $replace = false, $limit = null)
{ {
// 分析查询表达式 // 分析查询表达式
$options = $this->parseExpress(); $options = $this->parseExpress();
if (!is_array(reset($dataSet))) { if (!is_array(reset($dataSet))) {
return false; return false;
} }
// 生成SQL语句 // 生成SQL语句
$sql = $this->builder->insertAll($dataSet, $options, $replace); if (is_null($limit)) {
$sql = $this->builder->insertAll($dataSet, $options, $replace);
} else {
$array = array_chunk($dataSet, $limit, true);
foreach ($array as $item) {
$sql[] = $this->builder->insertAll($item, $options, $replace);
}
}
// 获取参数绑定 // 获取参数绑定
$bind = $this->getBind(); $bind = $this->getBind();
if ($options['fetch_sql']) { if ($options['fetch_sql']) {
// 获取实际执行的SQL语句 // 获取实际执行的SQL语句
return $this->connection->getRealSql($sql, $bind); return $this->connection->getRealSql($sql, $bind);
} elseif (is_array($sql)) {
// 执行操作
return $this->batchQuery($sql, $bind, $this);
} else { } else {
// 执行操作 // 执行操作
return $this->execute($sql, $bind); return $this->execute($sql, $bind, $this);
} }
} }
@ -2164,7 +2320,7 @@ class Query
return $this->connection->getRealSql($sql, $bind); return $this->connection->getRealSql($sql, $bind);
} else { } else {
// 执行操作 // 执行操作
return $this->execute($sql, $bind); return $this->execute($sql, $bind, $this);
} }
} }
@ -2231,7 +2387,7 @@ class Query
Cache::clear($options['cache']['tag']); Cache::clear($options['cache']['tag']);
} }
// 执行操作 // 执行操作
$result = '' == $sql ? 0 : $this->execute($sql, $bind); $result = '' == $sql ? 0 : $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
if (is_string($pk) && isset($where[$pk])) { if (is_string($pk) && isset($where[$pk])) {
$data[$pk] = $where[$pk]; $data[$pk] = $where[$pk];
@ -2300,7 +2456,7 @@ class Query
// 判断查询缓存 // 判断查询缓存
$cache = $options['cache']; $cache = $options['cache'];
unset($options['cache']); unset($options['cache']);
$key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options) . serialize($this->bind)); $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind));
$resultSet = Cache::get($key); $resultSet = Cache::get($key);
} }
if (false === $resultSet) { if (false === $resultSet) {
@ -2334,11 +2490,10 @@ class Query
// 数据列表读取后的处理 // 数据列表读取后的处理
if (!empty($this->model)) { if (!empty($this->model)) {
// 生成模型对象 // 生成模型对象
$modelName = $this->model;
if (count($resultSet) > 0) { if (count($resultSet) > 0) {
foreach ($resultSet as $key => $result) { foreach ($resultSet as $key => $result) {
/** @var Model $model */ /** @var Model $model */
$model = new $modelName($result); $model = $this->model->newInstance($result);
$model->isUpdate(true); $model->isUpdate(true);
// 关联查询 // 关联查询
@ -2358,7 +2513,7 @@ class Query
// 模型数据集转换 // 模型数据集转换
$resultSet = $model->toCollection($resultSet); $resultSet = $model->toCollection($resultSet);
} else { } else {
$resultSet = (new $modelName)->toCollection($resultSet); $resultSet = $this->model->toCollection($resultSet);
} }
} elseif ('collection' == $this->connection->getConfig('resultset_type')) { } elseif ('collection' == $this->connection->getConfig('resultset_type')) {
// 返回Collection对象 // 返回Collection对象
@ -2402,10 +2557,16 @@ class Query
} elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) { } elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) {
$data = $value[1]; $data = $value[1];
} }
$prefix = $this->connection->getConfig('database') . '.';
if (isset($data)) { if (isset($data)) {
return 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} else { }
return md5(serialize($options) . serialize($bind));
try {
return md5($prefix . serialize($options) . serialize($bind));
} catch (\Exception $e) {
throw new Exception('closure not support cache(true)');
} }
} }
@ -2442,11 +2603,11 @@ class Query
// 判断查询缓存 // 判断查询缓存
$cache = $options['cache']; $cache = $options['cache'];
if (true === $cache['key'] && !is_null($data) && !is_array($data)) { if (true === $cache['key'] && !is_null($data) && !is_array($data)) {
$key = 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; $key = 'think:' . $this->connection->getConfig('database') . '.' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} elseif (is_string($cache['key'])) { } elseif (is_string($cache['key'])) {
$key = $cache['key']; $key = $cache['key'];
} elseif (!isset($key)) { } elseif (!isset($key)) {
$key = md5(serialize($options) . serialize($this->bind)); $key = md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind));
} }
$result = Cache::get($key); $result = Cache::get($key);
} }
@ -2484,7 +2645,7 @@ class Query
$result = isset($resultSet[0]) ? $resultSet[0] : null; $result = isset($resultSet[0]) ? $resultSet[0] : null;
} }
if (isset($cache) && false !== $result) { if (isset($cache) && $result) {
// 缓存数据 // 缓存数据
$this->cacheData($key, $result, $cache); $this->cacheData($key, $result, $cache);
} }
@ -2494,8 +2655,7 @@ class Query
if (!empty($result)) { if (!empty($result)) {
if (!empty($this->model)) { if (!empty($this->model)) {
// 返回模型对象 // 返回模型对象
$model = $this->model; $result = $this->model->newInstance($result);
$result = new $model($result);
$result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null); $result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null);
// 关联查询 // 关联查询
if (!empty($options['relation'])) { if (!empty($options['relation'])) {
@ -2526,7 +2686,8 @@ class Query
protected function throwNotFound($options = []) protected function throwNotFound($options = [])
{ {
if (!empty($this->model)) { if (!empty($this->model)) {
throw new ModelNotFoundException('model data Not Found:' . $this->model, $this->model, $options); $class = get_class($this->model);
throw new ModelNotFoundException('model data Not Found:' . $class, $class, $options);
} else { } else {
$table = is_array($options['table']) ? key($options['table']) : $options['table']; $table = is_array($options['table']) ? key($options['table']) : $options['table'];
throw new DataNotFoundException('table data not Found:' . $table, $table, $options); throw new DataNotFoundException('table data not Found:' . $table, $table, $options);
@ -2574,48 +2735,54 @@ class Query
public function chunk($count, $callback, $column = null, $order = 'asc') public function chunk($count, $callback, $column = null, $order = 'asc')
{ {
$options = $this->getOptions(); $options = $this->getOptions();
if (isset($options['table'])) { if (empty($options['table'])) {
$table = is_array($options['table']) ? key($options['table']) : $options['table']; $options['table'] = $this->getTable();
} else {
$table = '';
}
$column = $column ?: $this->getPk($table);
if (is_array($column)) {
$column = $column[0];
} }
$column = $column ?: $this->getPk($options);
if (isset($options['order'])) { if (isset($options['order'])) {
if (App::$debug) { if (App::$debug) {
throw new \LogicException('chunk not support call order'); throw new \LogicException('chunk not support call order');
} }
unset($options['order']); unset($options['order']);
} }
$bind = $this->bind; $bind = $this->bind;
$resultSet = $this->options($options)->limit($count)->order($column, $order)->select(); if (is_array($column)) {
if (strpos($column, '.')) { $times = 1;
list($alias, $key) = explode('.', $column); $query = $this->options($options)->page($times, $count);
} else { } else {
$key = $column; if (strpos($column, '.')) {
} list($alias, $key) = explode('.', $column);
if ($resultSet instanceof Collection) { } else {
$resultSet = $resultSet->all(); $key = $column;
}
while (!empty($resultSet)) {
if (false === call_user_func($callback, $resultSet)) {
return false;
} }
$end = end($resultSet); $query = $this->options($options)->limit($count);
$lastId = is_array($end) ? $end[$key] : $end->$key; }
$resultSet = $this->options($options) $resultSet = $query->order($column, $order)->select();
->limit($count)
->bind($bind) while (count($resultSet) > 0) {
->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId)
->order($column, $order)
->select();
if ($resultSet instanceof Collection) { if ($resultSet instanceof Collection) {
$resultSet = $resultSet->all(); $resultSet = $resultSet->all();
} }
if (false === call_user_func($callback, $resultSet)) {
return false;
}
if (is_array($column)) {
$times++;
$query = $this->options($options)->page($times, $count);
} else {
$end = end($resultSet);
$lastId = is_array($end) ? $end[$key] : $end->getData($key);
$query = $this->options($options)
->limit($count)
->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId);
}
$resultSet = $query->bind($bind)->order($column, $order)->select();
} }
return true; return true;
} }
@ -2692,7 +2859,7 @@ class Query
Cache::clear($options['cache']['tag']); Cache::clear($options['cache']['tag']);
} }
// 执行操作 // 执行操作
$result = $this->execute($sql, $bind); $result = $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) {
list($a, $val) = explode('|', $key); list($a, $val) = explode('|', $key);
@ -2777,6 +2944,10 @@ class Query
} }
} }
if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) {
$options['master'] = true;
}
foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) { foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) {
if (!isset($options[$name])) { if (!isset($options[$name])) {
$options[$name] = ''; $options[$name] = '';

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -12,28 +12,93 @@
namespace think\db\builder; namespace think\db\builder;
use think\db\Builder; use think\db\Builder;
use think\Exception;
/** /**
* mysql数据库驱动 * mysql数据库驱动
*/ */
class Mysql extends Builder class Mysql extends Builder
{ {
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/**
* 生成insertall SQL
* @access public
* @param array $dataSet 数据集
* @param array $options 表达式
* @param bool $replace 是否replace
* @return string
* @throws Exception
*/
public function insertAll($dataSet, $options = [], $replace = false)
{
// 获取合法的字段
if ('*' == $options['field']) {
$fields = array_keys($this->query->getFieldsType($options['table']));
} else {
$fields = $options['field'];
}
foreach ($dataSet as $data) {
foreach ($data as $key => $val) {
if (!in_array($key, $fields, true)) {
if ($options['strict']) {
throw new Exception('fields not exists:[' . $key . ']');
}
unset($data[$key]);
} elseif (is_null($val)) {
$data[$key] = 'NULL';
} elseif (is_scalar($val)) {
$data[$key] = $this->parseValue($val, $key);
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入
$data[$key] = $val->__toString();
} else {
// 过滤掉非标量数据
unset($data[$key]);
}
}
$value = array_values($data);
$values[] = '( ' . implode(',', $value) . ' )';
if (!isset($insertFields)) {
$insertFields = array_map([$this, 'parseKey'], array_keys($data));
}
}
return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options),
implode(' , ', $insertFields),
implode(' , ', $values),
$this->parseComment($options['comment']),
], $this->insertAllSql);
}
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) { if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持 // JSON字段支持
list($field, $name) = explode('$.', $key); list($field, $name) = explode('$.', $key);
$key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; return 'json_extract(' . $field . ', \'$.' . $name . '\')';
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) { if ('__TABLE__' == $table) {
@ -43,7 +108,8 @@ class Mysql extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
$key = '`' . $key . '`'; $key = '`' . $key . '`';
} }
if (isset($table)) { if (isset($table)) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -44,12 +44,18 @@ class Pgsql extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) { if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持 // JSON字段支持

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -52,12 +52,18 @@ class Sqlite extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '.')) { if (strpos($key, '.')) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);

View File

@ -12,6 +12,7 @@
namespace think\db\builder; namespace think\db\builder;
use think\db\Builder; use think\db\Builder;
use think\db\Expression;
/** /**
* Sqlsrv数据库驱动 * Sqlsrv数据库驱动
@ -34,25 +35,29 @@ class Sqlsrv extends Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
$array = []; return ' ORDER BY rand()';
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
} }
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options, true) . ' ' . $sort;
}
}
return ' ORDER BY ' . implode(',', $array);
} }
/** /**
@ -68,12 +73,17 @@ class Sqlsrv extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);
@ -84,7 +94,7 @@ class Sqlsrv extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
$key = '[' . $key . ']'; $key = '[' . $key . ']';
} }
if (isset($table)) { if (isset($table)) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------