初始化项目

This commit is contained in:
2016-06-21 17:12:08 +08:00
commit 7ea154d684
903 changed files with 226100 additions and 0 deletions

View File

@@ -0,0 +1,717 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db;
use PDO;
use think\Db;
use think\db\Connection;
use think\db\Query;
use think\Exception;
abstract class Builder
{
// connection对象实例
protected $connection;
// 查询对象实例
protected $query;
// 查询参数
protected $options = [];
// 数据库表达式
protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME'];
// SQL表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/**
* 架构函数
* @access public
* @param Connection $connection 数据库连接对象实例
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* 设置当前的Query对象实例
* @access protected
* @param Query $query 当前查询对象实例
* @return void
*/
public function setQuery(Query $query)
{
$this->query = $query;
}
/**
* 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名小写
* @access protected
* @param string $sql sql语句
* @return string
*/
protected function parseSqlTable($sql)
{
return $this->query->parseSqlTable($sql);
}
/**
* 数据分析
* @access protected
* @param array $data 数据
* @param array $options 查询参数
* @return array
*/
protected function parseData($data, $options)
{
if (empty($data)) {
return [];
}
// 获取绑定信息
$bind = $this->query->getTableInfo($options['table'], 'bind');
if ('*' == $options['field']) {
$fields = array_keys($bind);
} else {
$fields = $options['field'];
}
$result = [];
foreach ($data as $key => $val) {
if (!in_array($key, $fields, true)) {
if ($options['strict']) {
throw new Exception('fields not exists:[' . $key . ']');
}
} else {
$item = $this->parseKey($key);
if (isset($val[0]) && 'exp' == $val[0]) {
$result[$item] = $val[1];
} elseif (is_null($val)) {
$result[$item] = 'NULL';
} elseif (is_scalar($val)) {
// 过滤非标量数据
if ($this->query->isBind(substr($val, 1))) {
$result[$item] = $val;
} else {
$this->query->bind($key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
$result[$item] = ':' . $key;
}
}
}
}
return $result;
}
/**
* 字段名分析
* @access protected
* @param string $key
* @return string
*/
protected function parseKey($key)
{
return $key;
}
/**
* value分析
* @access protected
* @param mixed $value
* @param string $field
* @return string|array
*/
protected function parseValue($value, $field = '')
{
if (is_string($value)) {
$value = strpos($value, ':') === 0 && $this->query->isBind(substr($value, 1)) ? $value : $this->connection->quote($value);
} elseif (is_array($value) && is_string($value[0]) && strtolower($value[0]) == 'exp') {
$value = $value[1];
} elseif (is_array($value)) {
$value = array_map([$this, 'parseValue'], $value);
} elseif (is_bool($value)) {
$value = $value ? '1' : '0';
} elseif (is_null($value)) {
$value = 'null';
}
return $value;
}
/**
* field分析
* @access protected
* @param mixed $fields
* @return string
*/
protected function parseField($fields)
{
if ('*' == $fields || empty($fields)) {
$fieldsStr = '*';
} elseif (is_array($fields)) {
// 支持 'field1'=>'field2' 这样的字段别名定义
$array = [];
foreach ($fields as $key => $field) {
if (!is_numeric($key)) {
$array[] = $this->parseKey($key) . ' AS ' . $this->parseKey($field);
} else {
$array[] = $this->parseKey($field);
}
}
$fieldsStr = implode(',', $array);
}
return $fieldsStr;
}
/**
* table分析
* @access protected
* @param mixed $table
* @return string
*/
protected function parseTable($tables)
{
if (is_array($tables)) {
// 支持别名定义
foreach ($tables as $table => $alias) {
$array[] = !is_numeric($table) ?
$this->parseKey($table) . ' ' . $this->parseKey($alias) :
$this->parseKey($alias);
}
$tables = $array;
} elseif (is_string($tables)) {
$tables = $this->parseSqlTable($tables);
$tables = array_map([$this, 'parseKey'], explode(',', $tables));
}
return implode(',', $tables);
}
/**
* where分析
* @access protected
* @param mixed $where
* @return string
*/
protected function parseWhere($where, $table)
{
$whereStr = $this->buildWhere($where, $table);
return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
}
/**
* 生成查询条件SQL
* @access public
* @param mixed $where
* @param string $table
* @return string
*/
public function buildWhere($where, $table)
{
if (empty($where)) {
$where = [];
}
if ($where instanceof Query) {
return $this->buildWhere($where->getOptions('where'), $table);
}
$whereStr = '';
// 获取字段信息
$fields = $this->query->getTableInfo($table, 'fields');
$binds = $this->query->getTableInfo($table, 'bind');
foreach ($where as $key => $val) {
$str = [];
foreach ($val as $field => $value) {
if ($fields && in_array($field, $fields, true) && is_scalar($value) && !$this->query->isBind($field)) {
$this->query->bind($field, $value, isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR);
$value = ':' . $field;
}
if ($value instanceof \Closure) {
// 使用闭包查询
$query = new Query($this->connection);
call_user_func_array($value, [ & $query]);
$str[] = ' ' . $key . ' ( ' . $this->buildWhere($query->getOptions('where'), $table) . ' )';
} else {
if (strpos($field, '|')) {
// 不同字段使用相同查询条件OR
$array = explode('|', $field);
$item = [];
foreach ($array as $k) {
$item[] = $this->parseWhereItem($k, $value);
}
$str[] = ' ' . $key . ' ( ' . implode(' OR ', $item) . ' )';
} elseif (strpos($field, '&')) {
// 不同字段使用相同查询条件AND
$array = explode('&', $field);
$item = [];
foreach ($array as $k) {
$item[] = $this->parseWhereItem($k, $value);
}
$str[] = ' ' . $key . ' ( ' . implode(' AND ', $item) . ' )';
} else {
// 对字段使用表达式查询
$field = is_string($field) ? $field : '';
$str[] = ' ' . $key . ' ' . $this->parseWhereItem($field, $value, $key);
}
}
}
$whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($key) + 1) : implode(' ', $str);
}
return $whereStr;
}
// where子单元分析
protected function parseWhereItem($field, $val, $rule = '')
{
// 字段分析
$key = $field ? $this->parseKey($field) : '';
// 查询规则和条件
if (!is_array($val)) {
$val = ['=', $val];
}
list($exp, $value) = $val;
// 对一个字段使用多个查询条件
if (is_array($exp)) {
$item = array_pop($val);
// 传入 or 或者 and
if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) {
$rule = $item;
} else {
array_push($val, $item);
}
foreach ($val as $item) {
$str[] = $this->parseWhereItem($key, $item, $rule);
}
return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
}
// 检测操作符
if (!in_array($exp, $this->exp)) {
$exp = strtolower($exp);
if (isset($this->exp[$exp])) {
$exp = $this->exp[$exp];
} else {
throw new Exception('where express error:' . $exp);
}
}
$whereStr = '';
if (in_array($exp, ['=', '<>', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE'])) {
// 比较运算 及 模糊匹配
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
} elseif ('EXP' == $exp) {
// 表达式查询
$whereStr .= '( ' . $key . ' ' . $value . ' )';
} elseif (in_array($exp, ['NOT NULL', 'NULL'])) {
// NULL 查询
$whereStr .= $key . ' IS ' . $exp;
} elseif (in_array($exp, ['NOT IN', 'IN'])) {
// IN 查询
if ($value instanceof \Closure) {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
} else {
$value = is_array($value) ? $value : explode(',', $value);
$zone = implode(',', $this->parseValue($value, $field));
$whereStr .= $key . ' ' . $exp . ' (' . $zone . ')';
}
} elseif (in_array($exp, ['NOT BETWEEN', 'BETWEEN'])) {
// BETWEEN 查询
$data = is_array($value) ? $value : explode(',', $value);
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($data[0], $field) . ' AND ' . $this->parseValue($data[1], $field);
} elseif (in_array($exp, ['NOT EXISTS', 'EXISTS'])) {
// EXISTS 查询
if ($value instanceof \Closure) {
$whereStr .= $exp . ' ' . $this->parseClosure($value);
} else {
$whereStr .= $exp . ' (' . $value . ')';
}
} elseif (in_array($exp, ['< TIME', '> TIME'])) {
$whereStr .= $key . ' ' . substr($exp, 0, 1) . ' ' . $this->parseDateTime($value, $field);
} elseif (in_array($exp, ['BETWEEN TIME', 'NOT BETWEEN TIME'])) {
if (is_string($value)) {
$value = explode(',', $value);
}
$whereStr .= $key . ' ' . substr($exp, 0, -4) . $this->parseDateTime($value[0], $field) . ' AND ' . $this->parseDateTime($value[1], $field);
}
return $whereStr;
}
// 执行闭包子查询
protected function parseClosure($call, $show = true)
{
$query = new Query($this->connection);
call_user_func_array($call, [ & $query]);
return $query->buildSql($show);
}
/**
* 日期时间条件解析
* @access protected
* @param string $value
* @param string $key
* @return string
*/
protected function parseDateTime($value, $key)
{
// 获取时间字段类型
$type = $this->query->getTableInfo('', 'type');
if (isset($type[$key])) {
$value = strtotime($value) ?: $value;
if (preg_match('/(datetime|timestamp)/is', $type[$key])) {
// 日期及时间戳类型
$value = date('Y-m-d H:i:s', $value);
} elseif (preg_match('/(date)/is', $type[$key])) {
// 日期及时间戳类型
$value = date('Y-m-d', $value);
}
}
return is_int($value) ? $value : $this->connection->quote($value);
}
/**
* limit分析
* @access protected
* @param mixed $lmit
* @return string
*/
protected function parseLimit($limit)
{
return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : '';
}
/**
* join分析
* @access protected
* @param mixed $join
* @return string
*/
protected function parseJoin($join)
{
$joinStr = '';
if (!empty($join)) {
$joinStr = ' ' . implode(' ', $join) . ' ';
}
return $joinStr;
}
/**
* order分析
* @access protected
* @param mixed $order
* @return string
*/
protected function parseOrder($order)
{
if (is_array($order)) {
$array = [];
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key) . ' ' . $sort;
}
}
$order = implode(',', $array);
}
return !empty($order) ? ' ORDER BY ' . $order : '';
}
/**
* group分析
* @access protected
* @param mixed $group
* @return string
*/
protected function parseGroup($group)
{
return !empty($group) ? ' GROUP BY ' . $group : '';
}
/**
* having分析
* @access protected
* @param string $having
* @return string
*/
protected function parseHaving($having)
{
return !empty($having) ? ' HAVING ' . $having : '';
}
/**
* comment分析
* @access protected
* @param string $comment
* @return string
*/
protected function parseComment($comment)
{
return !empty($comment) ? ' /* ' . $comment . ' */' : '';
}
/**
* distinct分析
* @access protected
* @param mixed $distinct
* @return string
*/
protected function parseDistinct($distinct)
{
return !empty($distinct) ? ' DISTINCT ' : '';
}
/**
* union分析
* @access protected
* @param mixed $union
* @return string
*/
protected function parseUnion($union)
{
if (empty($union)) {
return '';
}
$type = $union['type'];
unset($union['type']);
foreach ($union as $u) {
if ($u instanceof \Closure) {
$sql[] = $type . ' ' . $this->parseClosure($u, false);
} elseif (is_string($u)) {
$sql[] = $type . ' ' . $this->parseSqlTable($u);
}
}
return implode(' ', $sql);
}
/**
* index分析可在操作链中指定需要强制使用的索引
* @access protected
* @param mixed $index
* @return string
*/
protected function parseForce($index)
{
if (empty($index)) {
return '';
}
if (is_array($index)) {
$index = join(",", $index);
}
return sprintf(" FORCE INDEX ( %s ) ", $index);
}
/**
* 设置锁机制
* @access protected
* @param bool $locl
* @return string
*/
protected function parseLock($lock = false)
{
return $lock ? ' FOR UPDATE ' : '';
}
/**
* 生成查询SQL
* @access public
* @param array $options 表达式
* @return string
*/
public function select($options = [])
{
$sql = str_replace(
['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
[
$this->parseTable($options['table']),
$this->parseDistinct($options['distinct']),
$this->parseField($options['field']),
$this->parseJoin($options['join']),
$this->parseWhere($options['where'], $options['table']),
$this->parseGroup($options['group']),
$this->parseHaving($options['having']),
$this->parseOrder($options['order']),
$this->parseLimit($options['limit']),
$this->parseUnion($options['union']),
$this->parseLock($options['lock']),
$this->parseComment($options['comment']),
$this->parseForce($options['force']),
], $this->selectSql);
return $sql;
}
/**
* 生成insert SQL
* @access public
* @param array $data 数据
* @param array $options 表达式
* @param bool $replace 是否replace
* @return string
*/
public function insert(array $data, $options = [], $replace = false)
{
// 分析并处理数据
$data = $this->parseData($data, $options);
if (empty($data)) {
return 0;
}
$fields = array_keys($data);
$values = array_values($data);
$sql = str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table']),
implode(' , ', $fields),
implode(' , ', $values),
$this->parseComment($options['comment']),
], $this->insertSql);
return $sql;
}
/**
* 生成insertall SQL
* @access public
* @param array $dataSet 数据集
* @param array $options 表达式
* @return string
*/
public function insertAll($dataSet, $options)
{
// 获取合法的字段
if ('*' == $options['field']) {
$fields = $this->query->getTableInfo($options['table'], 'fields');
} 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_scalar($val)) {
$data[$key] = $this->parseValue($val, $key);
} else {
// 过滤掉非标量数据
unset($data[$key]);
}
}
$value = array_values($data);
$values[] = 'SELECT ' . implode(',', $value);
}
$fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet)));
$sql = str_replace(
['%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$this->parseTable($options['table']),
implode(' , ', $fields),
implode(' UNION ALL ', $values),
$this->parseComment($options['comment']),
], $this->insertAllSql);
return $sql;
}
/**
* 生成slectinsert SQL
* @access public
* @param array $fields 数据
* @param string $table 数据表
* @param array $options 表达式
* @return string
*/
public function selectInsert($fields, $table, $options)
{
if (is_string($fields)) {
$fields = explode(',', $fields);
}
$fields = array_map([$this, 'parseKey'], $fields);
$sql = 'INSERT INTO ' . $this->parseTable($table) . ' (' . implode(',', $fields) . ') ' . $this->select($options);
return $sql;
}
/**
* 生成update SQL
* @access public
* @param array $fields 数据
* @param array $options 表达式
* @return string
*/
public function update($data, $options)
{
$table = $this->parseTable($options['table']);
$data = $this->parseData($data, $options);
if (empty($data)) {
return '';
}
foreach ($data as $key => $val) {
$set[] = $key . '=' . $val;
}
$sql = str_replace(
['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
[
$this->parseTable($options['table']),
implode(',', $set),
$this->parseJoin($options['join']),
$this->parseWhere($options['where'], $options['table']),
$this->parseOrder($options['order']),
$this->parseLimit($options['limit']),
$this->parseLimit($options['lock']),
$this->parseComment($options['comment']),
], $this->updateSql);
return $sql;
}
/**
* 生成delete SQL
* @access public
* @param array $options 表达式
* @return string
*/
public function delete($options)
{
$sql = str_replace(
['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
[
$this->parseTable($options['table']),
!empty($options['using']) ? ' USING ' . $this->parseTable($options['using']) . ' ' : '',
$this->parseJoin($options['join']),
$this->parseWhere($options['where'], $options['table']),
$this->parseOrder($options['order']),
$this->parseLimit($options['limit']),
$this->parseLimit($options['lock']),
$this->parseComment($options['comment']),
], $this->deleteSql);
return $sql;
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* mysql数据库驱动
*/
class Mysql extends Builder
{
/**
* 字段和表名处理
* @access protected
* @param string $key
* @return string
*/
protected function parseKey($key)
{
$key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持
list($field, $name) = explode($key, '$.');
$key = 'jsn_extract(' . $field . ', \'$.\'.' . $name . ')';
}
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
$key = '`' . $key . '`';
}
return $key;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'rand()';
}
}

View File

@@ -0,0 +1,87 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\Db;
use think\db\Builder;
/**
* Oracle数据库驱动
*/
class Oracle extends Builder
{
protected $selectSql = 'SELECT * FROM (SELECT thinkphp.*, rownum AS numrow FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%) thinkphp ) %LIMIT%%COMMENT%';
/**
* limit
* @access public
* @return string
*/
public function parseLimit($limit)
{
$limitStr = '';
if (!empty($limit)) {
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr = "(numrow>" . $limit[0] . ") AND (numrow<=" . ($limit[0] + $limit[1]) . ")";
} else {
$limitStr = "(numrow>0 AND numrow<=" . $limit[0] . ")";
}
}
return $limitStr ? ' WHERE ' . $limitStr : '';
}
/**
* 设置锁机制
* @access protected
* @param bool|false $lock
* @return string
*/
protected function parseLock($lock = false)
{
if (!$lock) {
return '';
}
return ' FOR UPDATE NOWAIT ';
}
/**
* 字段和表名处理
* @access protected
* @param string $key
* @return string
*/
protected function parseKey($key)
{
$key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持
list($field, $name) = explode($key, '$.');
$key = $field . '."' . $name . '"';
}
return $key;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'DBMS_RANDOM.value';
}
}

View File

@@ -0,0 +1,69 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* Pgsql数据库驱动
*/
class Pgsql extends Builder
{
/**
* limit分析
* @access protected
* @param mixed $limit
* @return string
*/
public function parseLimit($limit)
{
$limitStr = '';
if (!empty($limit)) {
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
} else {
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
}
}
return $limitStr;
}
/**
* 字段和表名处理
* @access protected
* @param string $key
* @return string
*/
protected function parseKey($key)
{
$key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持
list($field, $name) = explode($key, '$.');
$key = $field . '->>\'' . $name . '\'';
}
return $key;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'RANDOM()';
}
}

View File

@@ -0,0 +1,51 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* Sqlite数据库驱动
*/
class Sqlite extends Builder
{
/**
* limit
* @access public
* @return string
*/
public function parseLimit($limit)
{
$limitStr = '';
if (!empty($limit)) {
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
} else {
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
}
}
return $limitStr;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'RANDOM()';
}
}

View File

@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* Sqlsrv数据库驱动
*/
class Sqlsrv extends Builder
{
protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%';
protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
/**
* order分析
* @access protected
* @param mixed $order
* @return string
*/
protected function parseOrder($order)
{
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'rand()';
}
/**
* 字段名分析
* @access protected
* @param string $key
* @return string
*/
protected function parseKey($key)
{
$key = trim($key);
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
$key = '[' . $key . ']';
}
return $key;
}
/**
* limit
* @access protected
* @param mixed $limit
* @return string
*/
protected function parseLimit($limit)
{
if (empty($limit)) {
return '';
}
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')';
} else {
$limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")";
}
return 'WHERE ' . $limitStr;
}
public function selectInsert($fields, $table, $options)
{
$this->selectSql=$this->selectInsertSql;
return parent::selectInsert($fields, $table, $options);
}
}

View File

@@ -0,0 +1,117 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\Connection;
use think\Log;
/**
* mysql数据库驱动
*/
class Mysql extends Connection
{
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config)
{
$dsn = 'mysql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
if (!empty($config['hostport'])) {
$dsn .= ';port=' . $config['hostport'];
} elseif (!empty($config['socket'])) {
$dsn .= ';unix_socket=' . $config['socket'];
}
if (!empty($config['charset'])) {
$dsn .= ';charset=' . $config['charset'];
}
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields($tableName)
{
$this->initConnect(true);
list($tableName) = explode(' ', $tableName);
if (strpos($tableName, '.')) {
$tableName = str_replace('.', '`.`', $tableName);
}
$sql = 'SHOW COLUMNS FROM `' . $tableName . '`';
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if ($result) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['field']] = [
'name' => $val['field'],
'type' => $val['type'],
'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
'default' => $val['default'],
'primary' => (strtolower($val['key']) == 'pri'),
'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables($dbName = '')
{
$sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
/**
* SQL性能分析
* @access protected
* @param string $sql
* @return array
*/
protected function getExplain($sql)
{
$pdo = $this->linkID->query("EXPLAIN " . $sql);
$result = $pdo->fetch(PDO::FETCH_ASSOC);
$result = array_change_key_case($result);
if (isset($result['extra'])) {
if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) {
Log::record('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn');
}
}
return $result;
}
protected function supportSavepoint(){
return true;
}
}

View File

@@ -0,0 +1,161 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\Db;
use think\db\Connection;
/**
* Oracle数据库驱动
*/
class Oracle extends Connection
{
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config)
{
$dsn = 'oci:dbname=';
if (!empty($config['hostname'])) {
// Oracle Instant Client
$dsn .= '//' . $config['hostname'] . ($config['hostport'] ? ':' . $config['hostport'] : '') . '/';
}
$dsn .= $config['database'];
if (!empty($config['charset'])) {
$dsn .= ';charset=' . $config['charset'];
}
return $dsn;
}
/**
* 执行语句
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param boolean $getLastInsID 是否获取自增ID
* @param string $sequence 序列名
* @return integer
* @throws \Exception
* @throws \think\Exception
*/
public function execute($sql, $bind = [], $getLastInsID = false, $sequence = null)
{
$this->initConnect(true);
if (!$this->linkID) {
return false;
}
// 根据参数绑定组装最终的SQL语句
$this->queryStr = $this->getBindSql($sql, $bind);
$flag = false;
if (preg_match("/^\s*(INSERT\s+INTO)\s+(\w+)\s+/i", $sql, $match)) {
if(is_null($sequence)){
$sequence = Config::get("db_sequence_prefix") . str_ireplace(Config::get("database.prefix"), "", $match[2]);
}
$flag = (boolean) $this->query("SELECT * FROM all_sequences WHERE sequence_name='" . strtoupper($sequence) . "'");
}
//释放前次的查询结果
if (!empty($this->PDOStatement)) {
$this->free();
}
Db::$executeTimes++;
try {
// 记录开始执行时间
$this->debug(true);
$this->PDOStatement = $this->linkID->prepare($sql);
// 参数绑定操作
$this->bindValue($bind);
$result = $this->PDOStatement->execute();
$this->debug(false);
$this->numRows = $this->PDOStatement->rowCount();
if ($flag || preg_match("/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i", $sql)) {
$this->lastInsID = $this->linkID->lastInsertId();
if ($getLastInsID) {
return $this->lastInsID;
}
}
return $this->numRows;
} catch (\PDOException $e) {
throw new \think\Exception($this->getError());
}
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields($tableName)
{
$this->initConnect(true);
list($tableName) = explode(' ', $tableName);
$sql = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . strtoupper($tableName) . "' ) b where table_name = '" . strtoupper($tableName) . "' and a.column_name = b.column_name (+)";
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if ($result) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['column_name']] = [
'name' => $val['column_name'],
'type' => $val['data_type'],
'notnull' => $val['notnull'],
'default' => $val['data_default'],
'primary' => $val['pk'],
'autoinc' => $val['pk'],
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息(暂时实现取得用户表信息)
* @access public
* @param string $dbName
* @return array
*/
public function getTables($dbName = '')
{
$pdo = $this->linkID->query("select table_name from all_tables");
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
/**
* SQL性能分析
* @access protected
* @param string $sql
* @return array
*/
protected function getExplain($sql)
{
return [];
}
protected function supportSavepoint(){
return true;
}
}

View File

@@ -0,0 +1,100 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\Connection;
/**
* Pgsql数据库驱动
*/
class Pgsql extends Connection
{
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config)
{
$dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
if (!empty($config['hostport'])) {
$dsn .= ';port=' . $config['hostport'];
}
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields($tableName)
{
$this->initConnect(true);
list($tableName) = explode(' ', $tableName);
$sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');';
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if ($result) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['field']] = [
'name' => $val['field'],
'type' => $val['type'],
'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
'default' => $val['default'],
'primary' => (strtolower($val['key']) == 'pri'),
'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables($dbName = '')
{
$sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'";
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
/**
* SQL性能分析
* @access protected
* @param string $sql
* @return array
*/
protected function getExplain($sql)
{
return [];
}
protected function supportSavepoint(){
return true;
}
}

View File

@@ -0,0 +1,99 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\Connection;
/**
* Sqlite数据库驱动
*/
class Sqlite extends Connection
{
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config)
{
$dsn = 'sqlite:' . $config['database'];
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields($tableName)
{
$this->initConnect(true);
list($tableName) = explode(' ', $tableName);
$sql = 'PRAGMA table_info( ' . $tableName . ' )';
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if ($result) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['name']] = [
'name' => $val['name'],
'type' => $val['type'],
'notnull' => 1 === $val['notnull'],
'default' => $val['dflt_value'],
'primary' => '1' == $val['pk'],
'autoinc' => '1' == $val['pk'],
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables($dbName = '')
{
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
. "UNION ALL SELECT name FROM sqlite_temp_master "
. "WHERE type='table' ORDER BY name";
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
/**
* SQL性能分析
* @access protected
* @param string $sql
* @return array
*/
protected function getExplain($sql)
{
return [];
}
protected function supportSavepoint(){
return true;
}
}

View File

@@ -0,0 +1,112 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\Connection;
/**
* Sqlsrv数据库驱动
*/
class Sqlsrv extends Connection
{
// PDO连接参数
protected $params = [
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
];
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config)
{
$dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname'];
if (!empty($config['hostport'])) {
$dsn .= ',' . $config['hostport'];
}
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields($tableName)
{
$this->initConnect(true);
list($tableName) = explode(' ', $tableName);
$sql = "SELECT column_name, data_type, column_default, is_nullable
FROM information_schema.tables AS t
JOIN information_schema.columns AS c
ON t.table_catalog = c.table_catalog
AND t.table_schema = c.table_schema
AND t.table_name = c.table_name
WHERE t.table_name = '$tableName'";
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if ($result) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['column_name']] = [
'name' => $val['column_name'],
'type' => $val['data_type'],
'notnull' => (bool) ('' === $val['is_nullable']), // not null is empty, null is yes
'default' => $val['column_default'],
'primary' => false,
'autoinc' => false,
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据表的字段信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables($dbName = '')
{
$sql = "SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
";
$pdo = $this->linkID->query($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
/**
* SQL性能分析
* @access protected
* @param string $sql
* @return array
*/
protected function getExplain($sql)
{
return [];
}
}

View File

@@ -0,0 +1,117 @@
CREATE OR REPLACE FUNCTION pgsql_type(a_type varchar) RETURNS varchar AS
$BODY$
DECLARE
v_type varchar;
BEGIN
IF a_type='int8' THEN
v_type:='bigint';
ELSIF a_type='int4' THEN
v_type:='integer';
ELSIF a_type='int2' THEN
v_type:='smallint';
ELSIF a_type='bpchar' THEN
v_type:='char';
ELSE
v_type:=a_type;
END IF;
RETURN v_type;
END;
$BODY$
LANGUAGE PLPGSQL;
CREATE TYPE "public"."tablestruct" AS (
"fields_key_name" varchar(100),
"fields_name" VARCHAR(200),
"fields_type" VARCHAR(20),
"fields_length" BIGINT,
"fields_not_null" VARCHAR(10),
"fields_default" VARCHAR(500),
"fields_comment" VARCHAR(1000)
);
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_schema_name varchar, a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
$body$
DECLARE
v_ret tablestruct;
v_oid oid;
v_sql varchar;
v_rec RECORD;
v_key varchar;
BEGIN
SELECT
pg_class.oid INTO v_oid
FROM
pg_class
INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid AND lower(pg_namespace.nspname) = a_schema_name)
WHERE
pg_class.relname=a_table_name;
IF NOT FOUND THEN
RETURN;
END IF;
v_sql='
SELECT
pg_attribute.attname AS fields_name,
pg_attribute.attnum AS fields_index,
pgsql_type(pg_type.typname::varchar) AS fields_type,
pg_attribute.atttypmod-4 as fields_length,
CASE WHEN pg_attribute.attnotnull THEN ''not null''
ELSE ''''
END AS fields_not_null,
pg_attrdef.adsrc AS fields_default,
pg_description.description AS fields_comment
FROM
pg_attribute
INNER JOIN pg_class ON pg_attribute.attrelid = pg_class.oid
INNER JOIN pg_type ON pg_attribute.atttypid = pg_type.oid
LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid AND pg_attrdef.adnum = pg_attribute.attnum
LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid AND pg_description.objsubid = pg_attribute.attnum
WHERE
pg_attribute.attnum > 0
AND attisdropped <> ''t''
AND pg_class.oid = ' || v_oid || '
ORDER BY pg_attribute.attnum' ;
FOR v_rec IN EXECUTE v_sql LOOP
v_ret.fields_name=v_rec.fields_name;
v_ret.fields_type=v_rec.fields_type;
IF v_rec.fields_length > 0 THEN
v_ret.fields_length:=v_rec.fields_length;
ELSE
v_ret.fields_length:=NULL;
END IF;
v_ret.fields_not_null=v_rec.fields_not_null;
v_ret.fields_default=v_rec.fields_default;
v_ret.fields_comment=v_rec.fields_comment;
SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
IF FOUND THEN
v_ret.fields_key_name=v_key;
ELSE
v_ret.fields_key_name='';
END IF;
RETURN NEXT v_ret;
END LOOP;
RETURN ;
END;
$body$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
COMMENT ON FUNCTION "public"."table_msg"(a_schema_name varchar, a_table_name varchar)
IS '获得表信息';
---
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
$body$
DECLARE
v_ret tablestruct;
BEGIN
FOR v_ret IN SELECT * FROM table_msg('public',a_table_name) LOOP
RETURN NEXT v_ret;
END LOOP;
RETURN;
END;
$body$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
COMMENT ON FUNCTION "public"."table_msg"(a_table_name varchar)
IS '获得表信息';

View File

@@ -0,0 +1,35 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
// +----------------------------------------------------------------------
namespace think\db\exception;
use think\exception\DbException;
/**
* PDO参数绑定异常
*/
class BindParamException extends DbException
{
/**
* BindParamException constructor.
* @param string $message
* @param array $config
* @param string $sql
* @param array $bind
* @param int $code
*/
public function __construct($message, $config, $sql, $bind, $code = 10502)
{
$this->setData('Bind Param', $bind);
parent::__construct($message, $config, $sql, $code);
}
}

View File

@@ -0,0 +1,43 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
// +----------------------------------------------------------------------
namespace think\db\exception;
use think\exception\DbException;
class DataNotFoundException extends DbException
{
protected $table;
/**
* DbException constructor.
* @param string $message
* @param string $table
* @param array $config
*/
public function __construct($message, $table = '', Array $config = [])
{
$this->message = $message;
$this->table = $table;
$this->setData('Database Config', $config);
}
/**
* 获取数据表名
* @access public
* @return string
*/
public function getTable()
{
return $this->table;
}
}

View File

@@ -0,0 +1,43 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
// +----------------------------------------------------------------------
namespace think\db\exception;
use think\exception\DbException;
class ModelNotFoundException extends DbException
{
protected $model;
/**
* 构造方法
* @param string $message
* @param string $model
*/
public function __construct($message, $model = '', Array $config = [])
{
$this->message = $message;
$this->model = $model;
$this->setData('Database Config', $config);
}
/**
* 获取模型类名
* @access public
* @return string
*/
public function getModel()
{
return $this->model;
}
}