<?php<liu21st@gmail.com>namespace Think\Db\Driver;
use Think\Db;
defined('THINK_PATH') or exit();
class Sqlsrv extends Db{
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%';
public function __construct($config='') {
if ( !function_exists('sqlsrv_connect') ) {
E(L('_NOT_SUPPERT_').':sqlsrv');
}
if(!empty($config)) {
$this->config = $config;
}
}
public function connect($config='',$linkNum=0) {
if ( !isset($this->linkID[$linkNum]) ) {
if(empty($config)) $config = $this->config;
$host = $config['hostname'].($config['hostport']?",{$config['hostport']}":'');
$connectInfo = array('Database'=>$config['database'],'UID'=>$config['username'],'PWD'=>$config['password'],'CharacterSet' => C('DEFAULT_CHARSET'));
$this->linkID[$linkNum] = sqlsrv_connect( $host, $connectInfo);
if ( !$this->linkID[$linkNum] ) $this->error(false);
$this->connected = true;
if(1 != C('DB_DEPLOY_TYPE')) unset($this->config);
}
return $this->linkID[$linkNum];
}
public function free() {
sqlsrv_free_stmt($this->queryID);
$this->queryID = null;
}
public function query($str,$bind=array()) {
$this->initConnect(false);
if ( !$this->_linkID ) return false;
if ( $this->queryID ) $this->free();
N('db_query',1);
G('queryStartTime');
$str = str_replace(array_keys($bind),'?',$str);
$bind = array_values($bind);
$this->queryStr = $str;
$this->queryID = sqlsrv_query($this->_linkID,$str,$bind, array( "Scrollable" => SQLSRV_CURSOR_KEYSET));
$this->debug();
if ( false === $this->queryID ) {
$this->error();
return false;
} else {
$this->numRows = sqlsrv_num_rows($this->queryID);
return $this->getAll();
}
}
public function execute($str,$bind=array()) {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
if ( $this->queryID ) $this->free();
N('db_write',1);
G('queryStartTime');
$str = str_replace(array_keys($bind),'?',$str);
$bind = array_values($bind);
$this->queryStr = $str;
$this->queryID= sqlsrv_query($this->_linkID,$str,$bind);
$this->debug();
if ( false === $this->queryID ) {
$this->error();
return false;
} else {
$this->numRows = sqlsrv_rows_affected($this->queryID);
$this->lastInsID = $this->mssql_insert_id();
return $this->numRows;
}
}
public function mssql_insert_id() {
$query = "SELECT @@IDENTITY as last_insert_id";
$result = sqlsrv_query($this->_linkID,$query);
list($last_insert_id) = sqlsrv_fetch_array($result);
sqlsrv_free_stmt($result);
return $last_insert_id;
}
public function startTrans() {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
if ($this->transTimes == 0) {
sqlsrv_begin_transaction($this->_linkID);
}
$this->transTimes++;
return ;
}
public function commit() {
if ($this->transTimes > 0) {
$result = sqlsrv_commit($this->_linkID);
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
public function rollback() {
if ($this->transTimes > 0) {
$result = sqlsrv_rollback($this->_linkID);
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
private function getAll() {
$result = array();
if($this->numRows >0) {
while($row = sqlsrv_fetch_array($this->queryID,SQLSRV_FETCH_ASSOC))
$result[] = $row;
}
return $result;
}
public function getFields($tableName) {
$result = $this->query("
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}'");
$pk = $this->query("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='{$tableName}'");
$info = array();
if($result) {
foreach ($result as $key => $val) {
$info[$val['column_name']] = array(
'name' => $val['column_name'],
'type' => $val['data_type'],
'notnull' => (bool) ($val['is_nullable'] === ''), 'default' => $val['column_default'],
'primary' => $val['column_name'] == $pk[0]['COLUMN_NAME'],
'autoinc' => false,
);
}
}
return $info;
}
public function getTables($dbName='') {
$result = $this->query("SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
");
$info = array();
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
protected function parseOrder($order) {
return !empty($order)? ' ORDER BY '.$order:' ORDER BY rand()';
}
protected function parseKey(&$key) {
$key = trim($key);
if(!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/',$key)) {
$key = '['.$key.']';
}
return $key;
}
public 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 update($data,$options) {
$this->model = $options['model'];
$sql = 'UPDATE '
.$this->parseTable($options['table'])
.$this->parseSet($data)
.$this->parseWhere(!empty($options['where'])?$options['where']:'')
.$this->parseLock(isset($options['lock'])?$options['lock']:false)
.$this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
}
public function delete($options=array()) {
$this->model = $options['model'];
$sql = 'DELETE FROM '
.$this->parseTable($options['table'])
.$this->parseWhere(!empty($options['where'])?$options['where']:'')
.$this->parseLock(isset($options['lock'])?$options['lock']:false)
.$this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
}
public function close() {
if ($this->_linkID){
sqlsrv_close($this->_linkID);
}
$this->_linkID = null;
}
public function error($result = true) {
$errors = sqlsrv_errors();
$this->error = '';
foreach( $errors as $error ) {
$this->error .= $error['code'].':'.$error['message'];
}
if('' != $this->queryStr){
$this->error .= "\n [ SQL语句 ] : ".$this->queryStr;
}
$result? trace($this->error,'','ERR'):E($this->error);
return $this->error;
}
}