改进对存储过程调用的支持 改进getRealSql的调用机制 改进数据表字段使用中划线的参数绑定支持

This commit is contained in:
thinkphp
2017-01-10 10:31:22 +08:00
parent 8f5fbe2e9f
commit 49108300c9
2 changed files with 92 additions and 45 deletions

View File

@@ -316,7 +316,7 @@ abstract class Builder
throw new Exception('where express error:' . $exp); throw new Exception('where express error:' . $exp);
} }
} }
$bindName = $bindName ?: 'where_' . str_replace('.', '_', $field); $bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);
if (preg_match('/\W/', $bindName)) { if (preg_match('/\W/', $bindName)) {
// 处理带非单词字符的字段名 // 处理带非单词字符的字段名
$bindName = md5($bindName); $bindName = md5($bindName);

View File

@@ -118,6 +118,9 @@ abstract class Connection
PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_EMULATE_PREPARES => false,
]; ];
// 绑定参数
protected $bind = [];
/** /**
* 架构函数 读取数据库配置信息 * 架构函数 读取数据库配置信息
* @access public * @access public
@@ -319,22 +322,26 @@ abstract class Connection
/** /**
* 执行查询 返回数据集 * 执行查询 返回数据集
* @access public * @access public
* @param string $sql sql指令 * @param string $sql sql指令
* @param array $bind 参数绑定 * @param array $bind 参数绑定
* @param boolean $master 是否在主服务器读操作 * @param bool $master 是否在主服务器读操作
* @param bool|string $class 指定返回的数据集对象 * @param bool $class 是否返回PDO对象
* @return mixed * @return mixed
* @throws BindParamException * @throws BindParamException
* @throws PDOException * @throws PDOException
*/ */
public function query($sql, $bind = [], $master = false, $class = false) public function query($sql, $bind = [], $master = false, $pdo = false)
{ {
$this->initConnect($master); $this->initConnect($master);
if (!$this->linkID) { if (!$this->linkID) {
return false; return false;
} }
// 根据参数绑定组装最终的SQL语句
$this->queryStr = $this->getRealSql($sql, $bind); // 记录SQL语句
$this->queryStr = $sql;
if ($bind) {
$this->bind = $bind;
}
//释放前次的查询结果 //释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) { if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
@@ -349,16 +356,22 @@ abstract class Connection
if (empty($this->PDOStatement)) { if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
} }
// 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定 // 参数绑定
$this->bindValue($bind); if ($procedure) {
$this->bindParam($bind);
} else {
$this->bindValue($bind);
}
// 执行查询 // 执行查询
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false);
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); // 返回结果集
return $this->getResult($class, $procedure); return $this->getResult($class, $procedure);
} catch (\PDOException $e) { } catch (\PDOException $e) {
throw new PDOException($e, $this->config, $this->queryStr); throw new PDOException($e, $this->config, $this->getLastsql());
} }
} }
@@ -377,8 +390,12 @@ abstract class Connection
if (!$this->linkID) { if (!$this->linkID) {
return false; return false;
} }
// 根据参数绑定组装最终的SQL语句
$this->queryStr = $this->getRealSql($sql, $bind); // 记录SQL语句
$this->queryStr = $sql;
if ($bind) {
$this->bind = $bind;
}
//释放前次的查询结果 //释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) { if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
@@ -393,8 +410,14 @@ abstract class Connection
if (empty($this->PDOStatement)) { if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
} }
// 参数绑定操作 // 是否为存储过程调用
$this->bindValue($bind); $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定
if ($procedure) {
$this->bindParam($bind);
} else {
$this->bindValue($bind);
}
// 执行语句 // 执行语句
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
@@ -403,7 +426,7 @@ abstract class Connection
$this->numRows = $this->PDOStatement->rowCount(); $this->numRows = $this->PDOStatement->rowCount();
return $this->numRows; return $this->numRows;
} catch (\PDOException $e) { } catch (\PDOException $e) {
throw new PDOException($e, $this->config, $this->queryStr); throw new PDOException($e, $this->config, $this->getLastsql());
} }
} }
@@ -416,23 +439,21 @@ abstract class Connection
*/ */
public function getRealSql($sql, array $bind = []) public function getRealSql($sql, array $bind = [])
{ {
if ($bind) { 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; if (PDO::PARAM_STR == $type) {
if (PDO::PARAM_STR == $type) { $value = $this->quote($value);
$value = $this->quote($value); } elseif (PDO::PARAM_INT == $type) {
} elseif (PDO::PARAM_INT == $type) { $value = (float) $value;
$value = (float) $value;
}
// 判断占位符
$sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) :
str_replace(
[':' . $key . ')', ':' . $key . ',', ':' . $key . ' '],
[$value . ')', $value . ',', $value . ' '],
$sql . ' ');
} }
// 判断占位符
$sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) :
str_replace(
[':' . $key . ')', ':' . $key . ',', ':' . $key . ' '],
[$value . ')', $value . ',', $value . ' '],
$sql . ' ');
} }
return rtrim($sql); return rtrim($sql);
} }
@@ -444,7 +465,7 @@ abstract class Connection
* @access public * @access public
* @param array $bind 要绑定的参数列表 * @param array $bind 要绑定的参数列表
* @return void * @return void
* @throws \think\Exception * @throws BindParamException
*/ */
protected function bindValue(array $bind = []) protected function bindValue(array $bind = [])
{ {
@@ -463,7 +484,34 @@ abstract class Connection
throw new BindParamException( throw new BindParamException(
"Error occurred when binding parameters '{$param}'", "Error occurred when binding parameters '{$param}'",
$this->config, $this->config,
$this->queryStr, $this->getLastsql(),
$bind
);
}
}
}
/**
* 存储过程的输入输出参数绑定
* @access public
* @param array $bind 要绑定的参数列表
* @return void
* @throws BindParamException
*/
protected function bindParam($bind)
{
foreach ($bind as $key => $val) {
if (is_numeric($key)) {
$key = $key + 1;
}
array_unshift($val, $key);
$result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val);
if (!$result) {
$param = array_shift($val);
throw new BindParamException(
"Error occurred when binding parameters '{$param}'",
$this->config,
$this->getLastsql(),
$bind $bind
); );
} }
@@ -473,19 +521,19 @@ abstract class Connection
/** /**
* 获得数据集 * 获得数据集
* @access protected * @access protected
* @param bool|string $class true 返回PDOStatement 字符串用于指定返回的类名 * @param bool $pdo 是否返回PDOStatement
* @param bool $procedure 是否存储过程 * @param bool $procedure 是否存储过程
* @return mixed * @return mixed
*/ */
protected function getResult($class = '', $procedure = false) protected function getResult($pdo = false, $procedure = false)
{ {
if (true === $class) { if ($pdo) {
// 返回PDOStatement对象处理 // 返回PDOStatement对象处理
return $this->PDOStatement; return $this->PDOStatement;
} }
if ($procedure) { if ($procedure) {
// 存储过程返回结果 // 存储过程返回结果
return $this->procedure($class); return $this->procedure();
} }
$result = $this->PDOStatement->fetchAll($this->fetchType); $result = $this->PDOStatement->fetchAll($this->fetchType);
$this->numRows = count($result); $this->numRows = count($result);
@@ -500,14 +548,13 @@ abstract class Connection
/** /**
* 获得存储过程数据集 * 获得存储过程数据集
* @access protected * @access protected
* @param bool|string $class true 返回PDOStatement 字符串用于指定返回的类名
* @return array * @return array
*/ */
protected function procedure($class) protected function procedure()
{ {
$item = []; $item = [];
do { do {
$result = $this->getResult($class); $result = $this->getResult();
if ($result) { if ($result) {
$item[] = $result; $item[] = $result;
} }
@@ -698,7 +745,7 @@ abstract class Connection
*/ */
public function getLastSql() public function getLastSql()
{ {
return $this->queryStr; return $this->getRealSql($this->queryStr, $this->bind);
} }
/** /**
@@ -736,7 +783,7 @@ abstract class Connection
$error = ''; $error = '';
} }
if ('' != $this->queryStr) { if ('' != $this->queryStr) {
$error .= "\n [ SQL语句 ] : " . $this->queryStr; $error .= "\n [ SQL语句 ] : " . $this->getLastsql();
} }
return $error; return $error;
} }
@@ -771,7 +818,7 @@ abstract class Connection
// 记录操作结束时间 // 记录操作结束时间
Debug::remark('queryEndTime', 'time'); Debug::remark('queryEndTime', 'time');
$runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime'); $runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime');
$sql = $sql ?: $this->queryStr; $sql = $sql ?: $this->getLastsql();
$log = $sql . ' [ RunTime:' . $runtime . 's ]'; $log = $sql . ' [ RunTime:' . $runtime . 's ]';
$result = []; $result = [];
// SQL性能分析 // SQL性能分析