<?php


// +----------------------------------------------------------------------


// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]


// +----------------------------------------------------------------------


// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.


// +----------------------------------------------------------------------


// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )


// +----------------------------------------------------------------------


// | Author: liu21st <liu21st@gmail.com>


// +----------------------------------------------------------------------





namespace Think\Db\Driver;


use Think\Db\Driver;





/**


 * mysql数据库驱动 


 */


class Mysql extends Driver{





    /**


     * 解析pdo连接的dsn信息


     * @access public


     * @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'])){


            //为兼容各版本PHP,用两种方式设置编码


            $this->options[\PDO::MYSQL_ATTR_INIT_COMMAND]    =   'SET NAMES '.$config['charset'];


            $dsn  .= ';charset='.$config['charset'];


        }


        return $dsn;


    }





    /**


     * 取得数据表的字段信息


     * @access public


     */


    public function getFields($tableName) {


        $this->initConnect(true);


        list($tableName) = explode(' ', $tableName);


        if(strpos($tableName,'.')){


        	list($dbName,$tableName) = explode('.',$tableName);


			$sql   = 'SHOW COLUMNS FROM `'.$dbName.'`.`'.$tableName.'`';


        }else{


        	$sql   = 'SHOW COLUMNS FROM `'.$tableName.'`';


        }


        


        $result = $this->query($sql);


        $info   =   array();


        if($result) {


            foreach ($result as $key => $val) {


				if(\PDO::CASE_LOWER != $this->_linkID->getAttribute(\PDO::ATTR_CASE)){


					$val = array_change_key_case ( $val ,  CASE_LOWER );


				}


                $info[$val['field']] = array(


                    '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 $info;


    }





    /**


     * 取得数据库的表信息


     * @access public


     */


    public function getTables($dbName='') {


        $sql    = !empty($dbName)?'SHOW TABLES FROM '.$dbName:'SHOW TABLES ';


        $result = $this->query($sql);


        $info   =   array();


        foreach ($result as $key => $val) {


            $info[$key] = current($val);


        }


        return $info;


    }





    /**


     * 字段和表名处理


     * @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;


    }





    /**


     * 批量插入记录


     * @access public


     * @param mixed $dataSet 数据集


     * @param array $options 参数表达式


     * @param boolean $replace 是否replace


     * @return false | integer


     */


    public function insertAll($dataSet,$options=array(),$replace=false) {


        $values  =  array();


        $this->model  =   $options['model'];


        if(!is_array($dataSet[0])) return false;


        $this->parseBind(!empty($options['bind'])?$options['bind']:array());


        $fields =   array_map(array($this,'parseKey'),array_keys($dataSet[0]));


        foreach ($dataSet as $data){


            $value   =  array();


            foreach ($data as $key=>$val){


                if(is_array($val) && 'exp' == $val[0]){


                    $value[]   =  $val[1];


                }elseif(is_null($val)){


                    $value[]   =   'NULL';


                }elseif(is_scalar($val)){


                    if(0===strpos($val,':') && in_array($val,array_keys($this->bind))){


                        $value[]   =   $this->parseValue($val);


                    }else{


                        $name       =   count($this->bind);


                        $value[]   =   ':'.$name;


                        $this->bindParam($name,$val);


                    }


                }


            }


            $values[]    = '('.implode(',', $value).')';


        }


        // 兼容数字传入方式


        $replace= (is_numeric($replace) && $replace>0)?true:$replace;


        $sql    =  (true===$replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES '.implode(',',$values).$this->parseDuplicate($replace);


        $sql    .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');


        return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);


    }





    /**


     * ON DUPLICATE KEY UPDATE 分析


     * @access protected


     * @param mixed $duplicate 


     * @return string


     */


    protected function parseDuplicate($duplicate){


        // 布尔值或空则返回空字符串


        if(is_bool($duplicate) || empty($duplicate)) return '';


        


        if(is_string($duplicate)){


        	// field1,field2 转数组


        	$duplicate = explode(',', $duplicate);


        }elseif(is_object($duplicate)){


        	// 对象转数组


        	$duplicate = get_class_vars($duplicate);


        }


        $updates                    = array();


        foreach((array) $duplicate as $key=>$val){


            if(is_numeric($key)){ // array('field1', 'field2', 'field3') 解析为 ON DUPLICATE KEY UPDATE field1=VALUES(field1), field2=VALUES(field2), field3=VALUES(field3)


                $updates[]          = $this->parseKey($val)."=VALUES(".$this->parseKey($val).")";


            }else{


                if(is_scalar($val)) // 兼容标量传值方式


                    $val            = array('value', $val);


                if(!isset($val[1])) continue;


                switch($val[0]){


                    case 'exp': // 表达式


                        $updates[]  = $this->parseKey($key)."=($val[1])";


                        break;


                    case 'value': // 值


                    default:


                        $name       = count($this->bind);


                        $updates[]  = $this->parseKey($key)."=:".$name;


                        $this->bindParam($name, $val[1]);


                        break;


                }


            }


        }


        if(empty($updates)) return '';


        return " ON DUPLICATE KEY UPDATE ".join(', ', $updates);


    }


    


	





    /**


     * 执行存储过程查询 返回多个数据集


     * @access public


     * @param string $str  sql指令


     * @param boolean $fetchSql  不执行只是获取SQL


     * @return mixed


     */


    public function procedure($str,$fetchSql=false) {


        $this->initConnect(false);


        $this->_linkID->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_WARNING);


        if ( !$this->_linkID ) return false;


        $this->queryStr     =   $str;


        if($fetchSql){


            return $this->queryStr;


        }


        //释放前次的查询结果


        if ( !empty($this->PDOStatement) ) $this->free();


        $this->queryTimes++;


        N('db_query',1); // 兼容代码


        // 调试开始


        $this->debug(true);


        $this->PDOStatement = $this->_linkID->prepare($str);


        if(false === $this->PDOStatement){


            $this->error();


            return false;


        }


        try{


            $result = $this->PDOStatement->execute();


            // 调试结束


            $this->debug(false);


            do


            {


                $result = $this->PDOStatement->fetchAll(\PDO::FETCH_ASSOC);


                if ($result)


                {


                    $resultArr[] = $result;


                }


            }


            while ($this->PDOStatement->nextRowset());


            $this->_linkID->setAttribute(\PDO::ATTR_ERRMODE, $this->options[\PDO::ATTR_ERRMODE]);


            return $resultArr;


        }catch (\PDOException $e) {


            $this->error();


            $this->_linkID->setAttribute(\PDO::ATTR_ERRMODE, $this->options[\PDO::ATTR_ERRMODE]);


            return false;


        }


    }


}


