<?php
/**
 * Project:     PHPPDO
 * File:        oci_statement.php
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * For questions, help, comments, discussion, etc.,
 * visit <http://devuni.com>
 *
 * @link http://devuni.com/
 * @Copyright 2007, 2008, 2009 Nikolay Ananiev.
 * @author Nikolay Ananiev <admin at devuni dot com>
 */

class phppdo_oci_statement extends phppdo_base_statement
{
    private $lobs = array();
    private $tmp_lobs = array();
    private $has_bound_lobs = false;

    public function __destruct()
    {
        // remove temp lobs
        foreach ($this->tmp_lobs as $file => $fp) {
            @fclose($fp);
            @unlink($file);
        }
    }

    public function bindColumn($column, &$param, $type = 0, $maxlen = 0, $driver_options = null)
    {
        if (parent::bindColumn($column, $param, $type, $maxlen, $driver_options)) {
            if ($type == PDO::PARAM_LOB) {
                $this->has_bound_lobs = true;
            }

            return true;
        }

        return false;
    }

    public function bindParam($parameter, &$variable, $data_type = -1, $length = 0, $driver_options = null)
    {
        if ($parameter[0] != ':' && !is_numeric($parameter)) {
            $parameter = ':'.$parameter;
        }

        if (!$length) {
            $length = -1;
        }

        $params_info = & $this->_params_info;

        switch ($data_type) {
            case PDO::PARAM_INT:
                $type = SQLT_INT;
            break;

            case PDO::PARAM_LOB:
                if (isset($params_info[$parameter])) {
                    $p = $params_info[$parameter];
                    $lob = oci_new_descriptor($this->_link, OCI_DTYPE_LOB);

                    if (oci_bind_by_name($this->_result, $p, $lob, -1, SQLT_BLOB)) {
                        $this->_bound_params[$p] = 1;
                        $this->lobs[$p] = array(&$lob, &$variable);

                        return true;
                    }

                    oci_free_descriptor($lob);
                }

                return false;

            break;

            default:
                $type = SQLT_CHR;
            break;
        }

        if (
            isset($params_info[$parameter]) &&
            oci_bind_by_name($this->_result, $params_info[$parameter], $variable, $length, $type)
        ) {
            $this->_bound_params[$params_info[$parameter]] = 1;

            return true;
        }

        return false;
    }

    public function closeCursor()
    {
        if ($this->_result) {
            oci_cancel($this->_result);
        }
    }

    public function columnCount()
    {
        if ($this->_result) {
            return oci_num_fields($this->_result);
        }

        return 0;
    }

    public function getColumnMeta($column)
    {
        if ($column >= $this->columnCount()) {
            return false;
        }

        $column++;
        $result = array();

        $result['native_type']  = oci_field_type($this->_result, $column);
        if (oci_field_is_null($this->_result, $column)) {
            $result['flags'] = 'is_null';
        }
        $result['name']         = oci_field_name($this->_result, $column);
        $result['len']          = oci_field_size($this->_result, $column);
        $result['precision']    = oci_field_precision($this->_result, $column).'.'.oci_field_scale($this->_result, $column);

        $result['pdo_type']     = PDO::PARAM_STR;

        return $result;
    }

    public function rowCount()
    {
        return oci_num_rows($this->_result);
    }

    protected function _execute()
    {
        if (!@oci_execute($this->_result, ($this->_driver->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT))) {
            $this->_set_stmt_error(null, PDO::ERRMODE_SILENT, 'execute');

            return false;
        }

        foreach ($this->lobs as $k => &$v) {
            $lob = & $v[0];
            $data = & $v[1];

            if (is_resource($data)) {
                while (!feof($data)) {
                    $lob->write(fread($data, 8192));
                }
            } else {
                $lob->write($data);
            }
        }

        if (0 < ($rows = $this->getAttribute(PDO::ATTR_PREFETCH))) {
            oci_set_prefetch($this->_result, $rows);
        }

        return true;
    }

    protected function _fetch_row()
    {
        // XXX: There seems to be a bug with oci_fetch_array($this->_result, (OCI_NUM + OCI_RETURN_LOBS))
        /*if($this->has_bound_lobs)
        {
            return @oci_fetch_row($this->_result);
        }

        return @oci_fetch_array($this->_result, (OCI_NUM + OCI_RETURN_LOBS));*/

        $row = @oci_fetch_row($this->_result);
        if (!$row) {
            return false;
        }

        if (!$this->has_bound_lobs) {
            foreach ($row as &$v) {
                if (is_object($v)) {
                    $v = $v->load();
                }
            }
        }

        return $row;
    }

    protected function _fetch_lob(&$p, &$col)
    {
        $tmp_file = tempnam(sys_get_temp_dir(), 'phppdo_');
        if (!$tmp_file) {
            return false;
        }

        if (is_object($col)) {
            $col->export($tmp_file);
        } else {
            return parent::_fetch_lob($p, $col);
        }

        $p = fopen($tmp_file, 'rb');
        if ($p) {
            $this->tmp_lobs[$tmp_file] = $p;
        }
    }

    protected function _field_name($field)
    {
        return oci_field_name($this->_result, ($field + 1));
    }

    protected function _table_name($field)
    {
        return '';
    }

    protected function _set_stmt_error($state = null, $mode = PDO::ERRMODE_SILENT, $func = '')
    {
        $error = oci_error($this->_result);

        if ($state === null) {
            $state = 'HY000';
        }
        $this->_set_error($error['code'], 'OCIStmtExecute: '.$error['message'], $state, $mode, $func);
    }
}
