<?php

class Cf_DB_pdo_driver extends CI_DB_pdo_driver
{
    public function __construct($params)
    {
        parent::__construct($params);
        log_message('debug', 'Extended DB driver class instantiated!');
    }

    /**
     * Non-persistent database connection
     *
     * @access  private called by the base class
     * @return  resource
     */
    public function db_connect($persistent = false)
    {
        return parent::db_connect($persistent);

        $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_EXCEPTION;
        $this->options['PDO::ATTR_EMULATE_PREPARES'] = false;

        try {
            return new PDO($this->dsn, $this->username, $this->password, $this->options);
        } catch (PDOException $e) {
            if ($this->db_debug && empty($this->failover)) {
                $this->display_error($e->getMessage(), '', true);
            }

            return false;
        }
    }
    // --------------------------------------------------------------------

    /**
     * Persistent database connection
     *
     * @access  private called by the base class
     * @return  resource
     */
    public function db_pconnect()
    {
        return parent::db_pconnect();

        $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_EXCEPTION;
        $this->options['PDO::ATTR_PERSISTENT'] = true;
        $this->options['PDO::ATTR_EMULATE_PREPARES'] = false;

        try {
            return new PDO($this->hostname, $this->username, $this->password, $this->options);
        } catch (PDOException $e) {
            if ($this->db_debug && empty($this->failover)) {
                $this->display_error($e->getMessage(), '', true);
            }

            return false;
        }
    }

    // --------------------------------------------------------------------

    /**
     * Execute the query
     *
     * Accepts an SQL string as input and returns a result object upon
     * successful execution of a "read" type query.  Returns boolean TRUE
     * upon successful execution of a "write" type query. Returns boolean
     * FALSE upon failure, and if the $db_debug variable is set to TRUE
     * will raise an error.
     *
     * @access  public
     * @param string  An SQL query string
     * @param array An array of binding data
     * @return  mixed
     */
    public function query($sql, $binds = false, $return_object = true)
    {
        return parent::query($sql, $binds, $return_object);

        if ($sql == '') {
            log_message('error', 'Invalid query: ' . $sql);
            if ($this->db_debug) {
                return $this->display_error('db_invalid_query');
            }
            return false;
        }

        // Verify table prefix and replace if necessary
        if ($this->dbprefix != '' and $this->swap_pre != '' and $this->dbprefix != $this->swap_pre) {
            $sql = preg_replace('/(\W)' . $this->swap_pre . '(\S+?)/', '\\1' . $this->dbprefix . '\\2', $sql);
        }

        // Compile binds if needed
        if ($binds !== false) {
            $sql = $this->compile_binds($sql, $binds);
        }

        // Is query caching enabled?  If the query is a "read type"
        // we will load the caching class and return the previously
        // cached query if it exists
        if ($this->cache_on == true and stristr($sql, 'SELECT')) {
            if ($this->_cache_init()) {
                $this->load_rdriver();
                if (false !== ($cache = $this->CACHE->read($sql))) {
                    return $cache;
                }
            }
        }

        // Save the  query for debugging
        if ($this->save_queries == true) {
            $this->queries[] = $sql;
        }

        // Start the Query Timer
        $time_start = [$sm, $ss] = explode(' ', microtime());

        // Run the Query
        if (false === ($this->result_id = $this->simple_query($sql))) {
            if ($this->save_queries == true) {
                $this->query_times[] = 0;
            }

            // This will trigger a rollback if transactions are being used
            $this->_trans_status = false;
            $error_no = $this->_error_number();
            $error_msg = $this->_error_message();

            // We call this function in order to roll-back queries
            // if transactions are enabled.  If we don't call this here
            // the error message will trigger an exit, causing the
            // transactions to remain in limbo.
            $this->trans_complete();

            // Log and display errors
            log_message('error', 'Query error: ' . $error_msg);

            if ($this->db_debug) {
                // grab the error number and message now, as we might run some
                // additional queries before displaying the error
                return $this->display_error(['Error Number: ' . $error_no, $error_msg, $sql]);
            }

            return false;
        }

        // Stop and aggregate the query time results
        $time_end = [$em, $es] = explode(' ', microtime());
        $this->benchmark += $em + $es - ($sm + $ss);

        if ($this->save_queries == true) {
            $this->query_times[] = $em + $es - ($sm + $ss);
        }

        // Increment the query counter
        $this->query_count++;

        // Was the query a "write" type?
        // If so we'll simply return true
        if ($this->is_write_type($sql) === true) {
            // If caching is enabled we'll auto-cleanup any
            // existing files related to this particular URI
            if ($this->cache_on == true and $this->cache_autodel == true and $this->_cache_init()) {
                $this->CACHE->delete();
            }

            return true;
        }

        // Return TRUE if we don't need to create a result object
        // Currently only the Oracle driver uses this when stored
        // procedures are used
        if ($return_object !== true) {
            return true;
        }

        // Load and instantiate the result driver

        $driver = $this->load_rdriver();
        $RES = new $driver();
        $RES->conn_id = $this->conn_id;
        $RES->result_id = $this->result_id;

        if ($this->dbdriver == 'oci8') {
            $RES->stmt_id = $this->stmt_id;
            $RES->curs_id = null;
            $RES->limit_used = $this->limit_used;
            $this->stmt_id = false;
        }

        // oci8 vars must be set before calling this
        $RES->num_rows = $RES->num_rows();

        // Is query caching enabled?  If so, we'll serialize the
        // result object and save it to a cache file.
        if ($this->cache_on == true and $this->_cache_init()) {
            // We'll create a new instance of the result object
            // only without the platform specific driver since
            // we can't use it with cached data (the query result
            // resource ID won't be any good once we've cached the
            // result object, so we'll have to compile the data
            // and save it)
            $CR = new CI_DB_result();
            $CR->num_rows = $RES->num_rows();
            $CR->result_object = $RES->result_object();
            $CR->result_array = $RES->result_array();

            // Reset these since cached objects can not utilize resource IDs.
            $CR->conn_id = null;
            $CR->result_id = null;

            $this->CACHE->write($sql, $CR);
        }

        return $RES;
    }

    /**
     * Execute the query
     *
     * @param string $sql SQL query
     * @return  mixed
     */
    public function _execute($sql)
    {
        return $this->conn_id->query($sql);
    }

    /**
     * Affected Rows
     *
     * @return  int
     */
    public function affected_rows()
    {
        return is_object($this->result_id) ? $this->result_id->rowCount() : 0;
    }

    public function display_error($error = '', $swap = '', $native = false)
    {
        if (PHP_SAPI === 'cli') {
            $error = is_array($error) ? implode(PHP_EOL, $error) : $error;
            printf('A database error occurred: %s', $error);
            exit();
        } else {
            parent::display_error($error, $swap, $native);
        }
    }
}
