복붙노트

[SQL] 어떻게 PDO 데이터베이스 쿼리를 디버깅하는 방법?

SQL

어떻게 PDO 데이터베이스 쿼리를 디버깅하는 방법?

PDO로 이동하기 전에, 나는 문자열을 연결하여 PHP에서 SQL 쿼리를 만들었습니다. 나는 데이터베이스 구문 오류를 가지고 있다면, 최종 SQL 쿼리 문자열을 에코 단지 수, 다음 코드로 내려 놔, 자신은 데이터베이스에 그것을 시도, 나는 오류를 고정 할 때까지 그것을 조정할.

준비된 PDO 문은 더 빠르고 더 안전하지만, 한 가지의 귀찮게 나 :이 데이터베이스로 전송됩니다으로 나는 최종 쿼리를 볼 수 없습니다. 내 아파치 로그 또는 내 사용자 지정 로그 파일 (내가 catch 블록 내부 오류 로그)에서 구문에 대한 오류를 얻을 때, 나는 그들에게 발생하는 쿼리를 볼 수 없습니다.

방법 캡처 파일로 데이터베이스에 PDO에서 전송과 로그 완전한 SQL 쿼리 있습니까?

해결법

  1. ==============================

    1.이 말 :

    이 말 :

    준비된 문을 사용하는 경우 음, 사실,에 "마지막 질문"같은 건 없다 :

    따라서, 귀하의 질문에 대답합니다 :

    번호 : 어디에도 "완전한 SQL 쿼리는"이 없기 때문에, 그것을 캡처 할 수있는 방법이 없습니다.

    디버깅 목적을 위해 할 수있는 가장 좋은 방법은, 명령문의 SQL 문자열로 값을 주입하여 "재 구조"에 "진짜"SQL 쿼리입니다.

    나는 보통 무엇을, 상황의이 종류이다 :

    이 디버깅에 올 때, 잘되지 않습니다 -하지만 준비된 명령문 그들이 가져 오는 장점의 가격입니다.

  2. ==============================

    2.파스칼 마틴은 PDO 번에 모든 데이터베이스의 전체 쿼리를 전송하지 않는 올바른 있지만, DB의 로깅 기능을 사용하기 ryeguy의 제안은 실제로 조립 및 데이터베이스에 의해 실행 나를는 전체 쿼리를 볼 수있었습니다.

    파스칼 마틴은 PDO 번에 모든 데이터베이스의 전체 쿼리를 전송하지 않는 올바른 있지만, DB의 로깅 기능을 사용하기 ryeguy의 제안은 실제로 조립 및 데이터베이스에 의해 실행 나를는 전체 쿼리를 볼 수있었습니다.

    방법은 다음과 같습니다 (이 지침은 Windows 시스템에서 MySQL을위한 - 귀하의 마일리지가 다를 수 있음)

    그 파일을 삭제하고 완료를 테스트있을 때 로깅을 해제해야합니다 이렇게 빨리 성장할 것입니다.

  3. ==============================

    3.물론이 모드를 사용하여 디버깅 할 수 있습니다 {{PDO :: ATTR_ERRMODE}} 디버그 라인을 보여줍니다 그럼 그냥 쿼리하기 전에 새로운 라인을 추가합니다.

    물론이 모드를 사용하여 디버깅 할 수 있습니다 {{PDO :: ATTR_ERRMODE}} 디버그 라인을 보여줍니다 그럼 그냥 쿼리하기 전에 새로운 라인을 추가합니다.

    $db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
    $db->query('SELECT *******');  
    
  4. ==============================

    4.아마 당신이하고 싶은) (사용 debugDumParams입니다 그것은 당신을 위해 준비된 문을 작성하지 않는다, 그러나 당신의 매개 변수를 표시합니다.

    아마 당신이하고 싶은) (사용 debugDumParams입니다 그것은 당신을 위해 준비된 문을 작성하지 않는다, 그러나 당신의 매개 변수를 표시합니다.

  5. ==============================

    5.이전 게시물 그러나 아마 다른 사람이 유용하게 찾을 수 있습니다;

    이전 게시물 그러나 아마 다른 사람이 유용하게 찾을 수 있습니다;

    function pdo_sql_debug($sql,$placeholders){
        foreach($placeholders as $k => $v){
            $sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql);
        }
        return $sql;
    }
    
  6. ==============================

    6.여기에 php.net에서 "마크"에 의해 코멘트에서 adpated 유효 SQL이 될 것입니다 무엇을 볼 수있는 기능이있다 :

    여기에 php.net에서 "마크"에 의해 코멘트에서 adpated 유효 SQL이 될 것입니다 무엇을 볼 수있는 기능이있다 :

    function sql_debug($sql_string, array $params = null) {
        if (!empty($params)) {
            $indexed = $params == array_values($params);
            foreach($params as $k=>$v) {
                if (is_object($v)) {
                    if ($v instanceof \DateTime) $v = $v->format('Y-m-d H:i:s');
                    else continue;
                }
                elseif (is_string($v)) $v="'$v'";
                elseif ($v === null) $v='NULL';
                elseif (is_array($v)) $v = implode(',', $v);
    
                if ($indexed) {
                    $sql_string = preg_replace('/\?/', $v, $sql_string, 1);
                }
                else {
                    if ($k[0] != ':') $k = ':'.$k; //add leading colon if it was left out
                    $sql_string = str_replace($k,$v,$sql_string);
                }
            }
        }
        return $sql_string;
    }
    
  7. ==============================

    7.제 PDO 쿼리는 클라이언트 측에서 준비되지 않습니다. PDO는 단순히 SQL 쿼리 및 데이터베이스 서버에 매개 변수를 보냅니다. 데이터베이스는 ( '?의 S) 대체를 수행하는 것이다. 당신은 두 가지 옵션이 있습니다 :

    제 PDO 쿼리는 클라이언트 측에서 준비되지 않습니다. PDO는 단순히 SQL 쿼리 및 데이터베이스 서버에 매개 변수를 보냅니다. 데이터베이스는 ( '?의 S) 대체를 수행하는 것이다. 당신은 두 가지 옵션이 있습니다 :

  8. ==============================

    8.거의 아무것도 오류가 확인 오류 로그를 제외하고 표시에 대해 말했다되었습니다, 하지만 오히려 도움이되는 기능이있다 :

    거의 아무것도 오류가 확인 오류 로그를 제외하고 표시에 대해 말했다되었습니다, 하지만 오히려 도움이되는 기능이있다 :

    <?php
    /* Provoke an error -- bogus SQL syntax */
    $stmt = $dbh->prepare('bogus sql');
    if (!$stmt) {
        echo "\PDO::errorInfo():\n";
        print_r($dbh->errorInfo());
    }
    ?>
    

    (소스 링크)

    이 코드는 예외 메시지로 사용하도록 수정 될 수 있다는 것은 자명 한 일이다 또는 오류의 다른 종류의 처리

  9. ==============================

    9.예를 들어, 당신은이 PDO 문이 :

    예를 들어, 당신은이 PDO 문이 :

    $query="insert into tblTest (field1, field2, field3)
    values (:val1, :val2, :val3)";
    $res=$db->prepare($query);
    $res->execute(array(
      ':val1'=>$val1,
      ':val2'=>$val2,
      ':val3'=>$val3,
    ));
    

    지금 당신은이 같은 배열을 정의하여 실행 쿼리를 얻을 수 있습니다 :

    $assoc=array(
      ':val1'=>$val1,
      ':val2'=>$val2,
      ':val3'=>$val3,
    );
    $exQuery=str_replace(array_keys($assoc), array_values($assoc), $query);
    echo $exQuery;
    
  10. ==============================

    10.인터넷 검색 나는 허용 솔루션으로이를 발견했다. 다른 클래스 대신 마법 함수 호출을 통해 호출되는 PDO와 PDO 기능으로 사용됩니다. 나는 이것이 성능에 심각한 문제를 야기 모르겠습니다. 분별있는 로깅 기능이 PDO에 추가 될 때까지하지만 사용할 수 있습니다.

    인터넷 검색 나는 허용 솔루션으로이를 발견했다. 다른 클래스 대신 마법 함수 호출을 통해 호출되는 PDO와 PDO 기능으로 사용됩니다. 나는 이것이 성능에 심각한 문제를 야기 모르겠습니다. 분별있는 로깅 기능이 PDO에 추가 될 때까지하지만 사용할 수 있습니다.

    그래서이 스레드에 따라, 당신은 당신이 오류가 발생하면 예외를 기록하고 발생 할 수 있습니다 PDO 연결에 대한 래퍼를 작성할 수 있습니다.

    여기에 간단한 예입니다 :

    class LoggedPDOSTatement extends PDOStatement    {
    
    function execute ($array)    {
        parent::execute ($array);
        $errors = parent::errorInfo();
        if ($errors[0] != '00000'):
            throw new Exception ($errors[2]);
        endif;
      }
    
    }
    

    그래서 당신은 대신 PDOStatement의 클래스를 사용할 수 있습니다 :

    $this->db->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('LoggedPDOStatement', array()));
    

    여기에 언급 된 PDO의 장식 구현 :

    class LoggedPDOStatement    {
    
    function __construct ($stmt)    {
        $this->stmt = $stmt;
    }
    
    function execute ($params = null)    {
        $result = $this->stmt->execute ($params); 
        if ($this->stmt->errorCode() != PDO::ERR_NONE):
            $errors = $this->stmt->errorInfo();
            $this->paint ($errors[2]);
        endif;
        return $result;
    }
    
    function bindValue ($key, $value)    {
        $this->values[$key] = $value;    
        return $this->stmt->bindValue ($key, $value);
    }
    
    function paint ($message = false)    {
        echo '<pre>';
        echo '<table cellpadding="5px">';
        echo '<tr><td colspan="2">Message: ' . $message . '</td></tr>';
        echo '<tr><td colspan="2">Query: ' . $this->stmt->queryString . '</td></tr>';
        if (count ($this->values) > 0):
        foreach ($this->values as $key => $value):
        echo '<tr><th align="left" style="background-color: #ccc;">' . $key . '</th><td>' . $value . '</td></tr>';
        endforeach;
        endif;
        echo '</table>';
        echo '</pre>';
    }
    
    function __call ($method, $params)    {
        return call_user_func_array (array ($this->stmt, $method), $params); 
    }
    
    }
    
  11. ==============================

    11.WAMP에 MySQL의를 기록하려면, 당신은 편집에있는 my.ini가 필요합니다 (예를 들어, 빈 \ WAMP에서 \ mysql을 \ mysql5.6.17 \의 my.ini)

    WAMP에 MySQL의를 기록하려면, 당신은 편집에있는 my.ini가 필요합니다 (예를 들어, 빈 \ WAMP에서 \ mysql을 \ mysql5.6.17 \의 my.ini)

    및 [mysqld를]에 추가

    general_log = 1
    general_log_file="c:\\tmp\\mysql.log"
    
  12. ==============================

    12.저는 여기에 "해결"매개 변수를 사용하여 SQL 쿼리를 반환하기 위해 만들어진 기능입니다.

    저는 여기에 "해결"매개 변수를 사용하여 SQL 쿼리를 반환하기 위해 만들어진 기능입니다.

    function paramToString($query, $parameters) {
        if(!empty($parameters)) {
            foreach($parameters as $key => $value) {
                preg_match('/(\?(?!=))/i', $query, $match, PREG_OFFSET_CAPTURE);
                $query = substr_replace($query, $value, $match[0][1], 1);
            }
        }
        return $query;
        $query = "SELECT email FROM table WHERE id = ? AND username = ?";
        $values = [1, 'Super'];
    
        echo paramToString($query, $values);
    

    이 같은 실행 가정

    $values = array(1, 'SomeUsername');
    $smth->execute($values);
    

    이 기능은 쿼리에 따옴표를 추가하지만 나를 위해 일을하지 않습니다.

  13. ==============================

    13.나는 목적을 debuging을위한 캐치 PDO 면제에 대한 해결책으로 한 문제는 "그것은 단지 PDO 면제 (대만족)를 잡았지만, PHP는 오류로 등록 된 구문 오류를 잡아하지 않았다 (이 있는지 이유는 모르겠지만,이다 왜 ") 솔루션 무관하다. 내 모든 PDO 호출 내가 모든 테이블에 내 모든 상호 작용을 연장하는 단일 테이블의 모델 클래스에서 온 ... 호출을 실행의 나 어디에 오류가 PHP 코드의 라인을 등록 때문에 내가 코드를 디버깅하려고했던이 복잡한 일이었다 라고하지만, 통화가 실제로에서 만들어지고 한 곳에 말하지 않았다. 나는이 문제를 해결하려면 다음 코드를 사용 :

    나는 목적을 debuging을위한 캐치 PDO 면제에 대한 해결책으로 한 문제는 "그것은 단지 PDO 면제 (대만족)를 잡았지만, PHP는 오류로 등록 된 구문 오류를 잡아하지 않았다 (이 있는지 이유는 모르겠지만,이다 왜 ") 솔루션 무관하다. 내 모든 PDO 호출 내가 모든 테이블에 내 모든 상호 작용을 연장하는 단일 테이블의 모델 클래스에서 온 ... 호출을 실행의 나 어디에 오류가 PHP 코드의 라인을 등록 때문에 내가 코드를 디버깅하려고했던이 복잡한 일이었다 라고하지만, 통화가 실제로에서 만들어지고 한 곳에 말하지 않았다. 나는이 문제를 해결하려면 다음 코드를 사용 :

    /**
     * Executes a line of sql with PDO.
     * 
     * @param string $sql
     * @param array $params
     */
    class TableModel{
        var $_db; //PDO connection
        var $_query; //PDO query
    
        function execute($sql, $params) { 
            //we're saving this as a global, so it's available to the error handler
            global $_tm;
            //setting these so they're available to the error handler as well
            $this->_sql = $sql;
            $this->_paramArray = $params;            
    
            $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->_query = $this->_db->prepare($sql);
    
            try {
                //set a custom error handler for pdo to catch any php errors
                set_error_handler('pdoErrorHandler');
    
                //save the table model object to make it available to the pdoErrorHandler
                $_tm = $this;
                $this->_query->execute($params);
    
                //now we restore the normal error handler
                restore_error_handler();
            } catch (Exception $ex) {
                pdoErrorHandler();
                return false;
            }            
        }
    }
    

    그래서, 위의 코드 어획량 BOTH PDO 예외와 PHP 구문 오류 및 취급 그들을 같은 방식으로. 내 오류 처리기는 다음과 같은 :

    function pdoErrorHandler() {
        //get all the stuff that we set in the table model
        global $_tm;
        $sql = $_tm->_sql;
        $params = $_tm->_params;
        $query = $tm->_query;
    
        $message = 'PDO error: ' . $sql . ' (' . implode(', ', $params) . ") \n";
    
        //get trace info, so we can know where the sql call originated from
        ob_start();
        debug_backtrace(); //I have a custom method here that parses debug backtrace, but this will work as well
        $trace = ob_get_clean();
    
        //log the error in a civilized manner
        error_log($message);
    
        if(admin(){
            //print error to screen based on your environment, logged in credentials, etc.
            print_r($message);
        }
    }
    

    사람이 전역 변수로 테이블 모델을 설정하는 것보다 내 오류 핸들러에 관련 정보를 얻는 방법에 대한 더 좋은 생각이 있다면, 나는 그것을 편집 내 코드를들을하실 수 있습니다.

  14. ==============================

    14.이 코드는 나를 위해 잘 작동합니다 :

    이 코드는 나를 위해 잘 작동합니다 :

    echo str_replace(array_keys($data), array_values($data), $query->queryString);
    

    당신의 이름으로 $ 데이터와 $ 쿼리를 대체하는 것을 잊지 마세요

  15. ==============================

    15.내가 PDO를 디버깅하는이 클래스를 사용 (Log4PHP 포함)

    내가 PDO를 디버깅하는이 클래스를 사용 (Log4PHP 포함)

    <?php
    
    /**
     * Extends PDO and logs all queries that are executed and how long
     * they take, including queries issued via prepared statements
     */
    class LoggedPDO extends PDO
    {
    
        public static $log = array();
    
        public function __construct($dsn, $username = null, $password = null, $options = null)
        {
            parent::__construct($dsn, $username, $password, $options);
        }
    
        public function query($query)
        {
            $result = parent::query($query);
            return $result;
        }
    
        /**
         * @return LoggedPDOStatement
         */
        public function prepare($statement, $options = NULL)
        {
            if (!$options) {
                $options = array();
            }
            return new \LoggedPDOStatement(parent::prepare($statement, $options));
        }
    }
    
    /**
     * PDOStatement decorator that logs when a PDOStatement is
     * executed, and the time it took to run
     * @see LoggedPDO
     */
    class LoggedPDOStatement
    {
    
        /**
         * The PDOStatement we decorate
         */
        private $statement;
        protected $_debugValues = null;
    
        public function __construct(PDOStatement $statement)
        {
            $this->statement = $statement;
        }
    
        public function getLogger()
        {
            return \Logger::getLogger('PDO sql');
        }
    
        /**
         * When execute is called record the time it takes and
         * then log the query
         * @return PDO result set
         */
        public function execute(array $params = array())
        {
            $start = microtime(true);
            if (empty($params)) {
                $result = $this->statement->execute();
            } else {
                foreach ($params as $key => $value) {
                    $this->_debugValues[$key] = $value;
                }
                $result = $this->statement->execute($params);
            }
    
            $this->getLogger()->debug($this->_debugQuery());
    
            $time = microtime(true) - $start;
            $ar = (int) $this->statement->rowCount();
            $this->getLogger()->debug('Affected rows: ' . $ar . ' Query took: ' . round($time * 1000, 3) . ' ms');
            return $result;
        }
    
        public function bindValue($parameter, $value, $data_type = false)
        {
            $this->_debugValues[$parameter] = $value;
            return $this->statement->bindValue($parameter, $value, $data_type);
        }
    
        public function _debugQuery($replaced = true)
        {
            $q = $this->statement->queryString;
    
            if (!$replaced) {
                return $q;
            }
    
            return preg_replace_callback('/:([0-9a-z_]+)/i', array($this, '_debugReplace'), $q);
        }
    
        protected function _debugReplace($m)
        {
            $v = $this->_debugValues[$m[0]];
    
            if ($v === null) {
                return "NULL";
            }
            if (!is_numeric($v)) {
                $v = str_replace("'", "''", $v);
            }
    
            return "'" . $v . "'";
        }
    
        /**
         * Other than execute pass all other calls to the PDOStatement object
         * @param string $function_name
         * @param array $parameters arguments
         */
        public function __call($function_name, $parameters)
        {
            return call_user_func_array(array($this->statement, $function_name), $parameters);
        }
    }
    
  16. ==============================

    16.나는 정확히 여기에 현대 작곡가로드 프로젝트 / 저장소를 만들었습니다 :

    나는 정확히 여기에 현대 작곡가로드 프로젝트 / 저장소를 만들었습니다 :

    여기에 프로젝트의 GitHub의 집 찾기 여기 설명하는 블로그 게시물을 참조하십시오. 한 줄은 composer.json에 추가 한 다음은 다음과 같이 사용할 수 있습니다 :

    echo debugPDO($sql, $parameters);
    

    $ SQL 원시 SQL 문이다, $ 매개 변수는 매개 변수의 배열입니다 : 키가 자리 표시 자 이름 ( "USER_ID") 또는 익명의 매개 변수 ( "?"), 값이 수 .. 음, 값.

    논리는 뒤에 :이 스크립트는 단순히 매개 변수를 마치고하고 제공하는 SQL 문자열로 교체합니다. 슈퍼 간단하지만 사용 사례의 99 % 슈퍼 효과적인. 참고 :이 그냥 기본 에뮬레이션이 아닌 실제 PDO 디버깅입니다 (PHP는 MySQL 서버가 구분 원시 SQL 및 매개 변수를 전송로이 수 없습니다 참조).

    이 스크립트 뒤에 기본적으로 전체 주요 기능을 쓰기 위해 PDO에서 원시 SQL 쿼리 문자열을 점점 StackOverflow의 스레드에서 bigwebguy 및 마이크에 큰 감사를드립니다. 큰까지!

  17. ==============================

    17.TL; DR 로그인 모든 쿼리와 꼬리 MySQL의 로그.

    TL; DR 로그인 모든 쿼리와 꼬리 MySQL의 로그.

    이러한 방향은 우분투 14.04의 설치 제에 대한 것입니다. -a lsb_release 문제 명령은 버전을 얻을 수 있습니다. 귀하 다를 수 있습니다 설치합니다.

    잘 했어! 당신은 모든 설정입니다. 당신은 PDO 실시간으로 앱 차종을 조회 볼 수 있도록 이제 모두 당신이해야 할 일은 꼬리 로그 파일입니다.

    이 cmd를 꼬리 -f /var/log/mysql/mysql.log를 입력합니다.

    귀하의 출력은 다음과 같이 보일 것입니다 :

    73 Connect  xyz@localhost on your_db
    73 Query    SET NAMES utf8mb4
    74 Connect  xyz@localhost on your_db
    75 Connect  xyz@localhost on your_db
    74 Quit 
    75 Prepare  SELECT email FROM customer WHERE email=? LIMIT ?
    75 Execute  SELECT email FROM customer WHERE email='a@b.co' LIMIT 5
    75 Close stmt   
    75 Quit 
    73 Quit 
    

    모든 새 앱의 차종이 자동으로 당신이 로그를 미행 계속 뷰에 나타납니다 쿼리합니다. 꼬리를 종료하려면, cmd를 / Ctrl 키 C를 누르십시오.

    inspo 위 나단 롱의 대답에 밖으로 큰 소리 우분투에서이를 알아낼 수 있습니다. 또한이 솔루션에 저를 이끌어 나단의 게시물에 자신의 의견에 대한 dikirill합니다.

    사랑은 당신에 유래!

  18. ==============================

    18.데비안의 nginx 환경에서 나는 다음과 같은했다.

    데비안의 nginx 환경에서 나는 다음과 같은했다.

    당신이 로그인 오류 = /var/log/mysql/error.log는 다음과 같은 2 개 라인을 노호 추가 발견하면 고토 편집 mysqld.cnf을 /etc/mysql/mysql.conf.d.

    general_log_file        = /var/log/mysql/mysql.log
    general_log             = 1
    

    mysql.log -f 로그 고토는 / var / log / MySQL과 꼬리를 참조하십시오

    당신이 빠르게 성장할 것이 로그 파일과 생산 환경 삭제 mysql.log에 거대한 될 수 있다면 디버깅 완료되면이 라인을 주석 처리해야합니다.

  19. from https://stackoverflow.com/questions/2411182/how-to-debug-pdo-database-queries by cc-by-sa and MIT license