summaryrefslogtreecommitdiffstats
path: root/system/database/drivers/oci8/oci8_result.php
diff options
context:
space:
mode:
authorAndrey Andreev <narf@bofh.bg>2012-04-27 10:54:40 +0200
committerAndrey Andreev <narf@bofh.bg>2012-04-27 10:54:40 +0200
commitce747c8a1e72a30f5369de7d56ba91b72f0e2eb3 (patch)
tree32433143610df1683131dba5cf4437ddadc306c6 /system/database/drivers/oci8/oci8_result.php
parent870ad190f068ec16aad45622528ba6747c61c120 (diff)
parent61318a2c53c13a314f483fcbbfd64c6e01f5242c (diff)
Merge upstream branch
Diffstat (limited to 'system/database/drivers/oci8/oci8_result.php')
-rw-r--r--system/database/drivers/oci8/oci8_result.php555
1 files changed, 508 insertions, 47 deletions
diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php
index a14e32eec..7b05e0a43 100644
--- a/system/database/drivers/oci8/oci8_result.php
+++ b/system/database/drivers/oci8/oci8_result.php
@@ -1,13 +1,13 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* CodeIgniter
*
* An open source application development framework for PHP 5.2.4 or newer
*
* NOTICE OF LICENSE
- *
+ *
* Licensed under the Open Software License version 3.0
- *
+ *
* This source file is subject to the Open Software License (OSL 3.0) that is
* bundled with this package in the files license.txt / license.rst. It is
* also available through the world wide web at this URL:
@@ -25,8 +25,6 @@
* @filesource
*/
-// ------------------------------------------------------------------------
-
/**
* oci8 Result Class
*
@@ -41,6 +39,12 @@ class CI_DB_oci8_result extends CI_DB_result {
public $stmt_id;
public $curs_id;
public $limit_used;
+ public $commit_mode;
+
+ /* Overwriting the parent here, so we have a way to know if it's
+ * already called or not:
+ */
+ public $num_rows;
public function __construct(&$driver_object)
{
@@ -48,30 +52,32 @@ class CI_DB_oci8_result extends CI_DB_result {
$this->stmt_id = $driver_object->stmt_id;
$this->curs_id = $driver_object->curs_id;
$this->limit_used = $driver_object->limit_used;
+ $this->commit_mode =& $driver_object->commit_mode;
$driver_object->stmt_id = FALSE;
}
/**
* Number of rows in the result set.
*
- * Oracle doesn't have a graceful way to retun the number of rows
+ * Oracle doesn't have a graceful way to return the number of rows
* so we have to use what amounts to a hack.
*
- *
- * @access public
- * @return integer
+ * @return int
*/
public function num_rows()
{
- if ($this->num_rows === 0 && count($this->result_array()) > 0)
+ if ( ! is_int($this->num_rows))
{
- $this->num_rows = count($this->result_array());
- @oci_execute($this->stmt_id, OCI_DEFAULT);
-
- if ($this->curs_id)
+ if (count($this->result_array) > 0)
{
- @oci_execute($this->curs_id, OCI_DEFAULT);
+ return $this->num_rows = count($this->result_array);
}
+ elseif (count($this->result_object) > 0)
+ {
+ return $this->num_rows = count($this->result_object);
+ }
+
+ return $this->num_rows = count($this->result_array());
}
return $this->num_rows;
@@ -82,20 +88,14 @@ class CI_DB_oci8_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
public function num_fields()
{
$count = @oci_num_fields($this->stmt_id);
// if we used a limit we subtract it
- if ($this->limit_used)
- {
- $count = $count - 1;
- }
-
- return $count;
+ return ($this->limit_used) ? $count - 1 : $count;
}
// --------------------------------------------------------------------
@@ -105,7 +105,6 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
public function list_fields()
@@ -125,18 +124,17 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
- * @return array
+ * @return array
*/
public function field_data()
{
$retval = array();
for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++)
{
- $F = new stdClass();
- $F->name = oci_field_name($this->stmt_id, $c);
- $F->type = oci_field_type($this->stmt_id, $c);
- $F->max_length = oci_field_size($this->stmt_id, $c);
+ $F = new stdClass();
+ $F->name = oci_field_name($this->stmt_id, $c);
+ $F->type = oci_field_type($this->stmt_id, $c);
+ $F->max_length = oci_field_size($this->stmt_id, $c);
$retval[] = $F;
}
@@ -149,7 +147,7 @@ class CI_DB_oci8_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
public function free_result()
{
@@ -158,6 +156,17 @@ class CI_DB_oci8_result extends CI_DB_result {
oci_free_statement($this->result_id);
$this->result_id = FALSE;
}
+
+ if (is_resource($this->stmt_id))
+ {
+ oci_free_statement($this->stmt_id);
+ }
+
+ if (is_resource($this->curs_id))
+ {
+ oci_cancel($this->curs_id);
+ $this->curs_id = NULL;
+ }
}
// --------------------------------------------------------------------
@@ -167,8 +176,7 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access protected
- * @return array
+ * @return array
*/
protected function _fetch_assoc()
{
@@ -183,22 +191,20 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access protected
- * @return object
+ * @return object
*/
protected function _fetch_object()
{
$id = ($this->curs_id) ? $this->curs_id : $this->stmt_id;
- return @oci_fetch_object($id);
+ return oci_fetch_object($id);
}
// --------------------------------------------------------------------
/**
- * Query result. "array" version.
+ * Query result. Array version.
*
- * @access public
- * @return array
+ * @return array
*/
public function result_array()
{
@@ -206,14 +212,429 @@ class CI_DB_oci8_result extends CI_DB_result {
{
return $this->result_array;
}
+ elseif (count($this->result_object) > 0)
+ {
+ for ($i = 0, $c = count($this->result_object); $i < $c; $i++)
+ {
+ $this->result_array[$i] = (array) $this->result_object[$i];
+ }
+
+ return $this->result_array;
+ }
+ elseif (is_array($this->row_data))
+ {
+ if (count($this->row_data) === 0)
+ {
+ return $this->result_array;
+ }
+ else
+ {
+ $row_index = count($this->row_data);
+ }
+ }
+ else
+ {
+ $row_index = 0;
+ $this->row_data = array();
+ }
$row = NULL;
while ($row = $this->_fetch_assoc())
{
- $this->result_array[] = $row;
+ $this->row_data[$row_index++] = $row;
}
- return $this->result_array;
+ return $this->result_array = $this->row_data;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Query result. "object" version.
+ *
+ * @return array
+ */
+ public function result_object()
+ {
+ if (count($this->result_object) > 0)
+ {
+ return $this->result_object;
+ }
+ elseif (count($this->result_array) > 0)
+ {
+ for ($i = 0, $c = count($this->result_array); $i < $c; $i++)
+ {
+ $this->result_object[] = (object) $this->result_array[$i];
+ }
+
+ return $this->result_object;
+ }
+ elseif (is_array($this->row_data))
+ {
+ if (count($this->row_data) === 0)
+ {
+ return $this->result_object;
+ }
+ else
+ {
+ $row_index = count($this->row_data);
+ for ($i = 0; $i < $row_index; $i++)
+ {
+ $this->result_object[$i] = (object) $this->row_data[$i];
+ }
+ }
+ }
+ else
+ {
+ $row_index = 0;
+ $this->row_data = array();
+ }
+
+ $row = NULL;
+ while ($row = $this->_fetch_object())
+ {
+ $this->row_data[$row_index] = (array) $row;
+ $this->result_object[$row_index++] = $row;
+ }
+
+ return $this->result_object;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Query result. Custom object version.
+ *
+ * @param string class name used to instantiate rows to
+ * @return array
+ */
+ public function custom_result_object($class_name)
+ {
+ if (isset($this->custom_result_object[$class_name]))
+ {
+ return $this->custom_result_object[$class_name];
+ }
+
+ if ( ! class_exists($class_name) OR $this->result_id === FALSE OR $this->num_rows() === 0)
+ {
+ return array();
+ }
+
+ /* Even if we didn't have result_array or result_object
+ * set prior to custom_result_object() being called,
+ * num_rows() has already done so.
+ * Pass by reference, as we don't know how
+ * large it might be and we don't want 1000 row
+ * sets being copied.
+ */
+ if (count($this->result_array) > 0)
+ {
+ $data = &$this->result_array;
+ }
+ elseif (count($this->result_object) > 0)
+ {
+ $data = &$this->result_object;
+ }
+
+ $this->custom_result_object[$class_name] = array();
+ for ($i = 0, $c = count($data); $i < $c; $i++)
+ {
+ $this->custom_result_object[$class_name][$i] = new $class_name();
+ foreach ($data[$i] as $key => $value)
+ {
+ $this->custom_result_object[$class_name][$i]->$key = $value;
+ }
+ }
+
+ return $this->custom_result_object[$class_name];
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Single row result.
+ *
+ * Acts as a wrapper for row_object(), row_array()
+ * and custom_row_object(). Also used by first_row(), next_row()
+ * and previous_row().
+ *
+ * @param int row index
+ * @param string ('object', 'array' or a custom class name)
+ * @return mixed whatever was passed to the second parameter
+ */
+ public function row($n = 0, $type = 'object')
+ {
+ if ($type === 'object')
+ {
+ return $this->row_object($n);
+ }
+ elseif ($type === 'array')
+ {
+ return $this->row_array($n);
+ }
+
+ return $this->custom_row_object($n, $type);
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Single row result. Array version.
+ *
+ * @param int row index
+ * @return array
+ */
+ public function row_array($n = 0)
+ {
+ // Make sure $n is not a string
+ if ( ! is_int($n))
+ {
+ $n = (int) $n;
+ }
+
+ /* If row_data is initialized, it means that we've already tried
+ * (at least) to fetch some data, so ... check if we already have
+ * this row.
+ */
+ if (is_array($this->row_data))
+ {
+ /* If we already have row_data[$n] - return it.
+ *
+ * If we enter the elseif, there's a number of reasons to
+ * return an empty array:
+ *
+ * - count($this->row_data) === 0 means there are no results
+ * - num_rows being set, result_array and/or result_object
+ * having count() > 0 means that we've already fetched all
+ * data and $n is greater than our highest row index available
+ * - $n < $this->current_row means that if such row existed,
+ * we would've already returned it, therefore $n is an
+ * invalid index
+ */
+ if (isset($this->row_data[$n])) // We already have this row
+ {
+ $this->current_row = $n;
+ return $this->row_data[$n];
+ }
+ elseif (count($this->row_data) === 0 OR is_int($this->num_rows)
+ OR count($this->result_array) > 0 OR count($this->result_object) > 0
+ OR $n < $this->current_row)
+ {
+ // No such row exists
+ return array();
+ }
+
+ // Get the next row index that would actually need to be fetched
+ $current_row = ($this->current_row < count($this->row_data)) ? count($this->row_data) : $this->current_row + 1;
+ }
+ else
+ {
+ $current_row = $this->current_row = 0;
+ $this->row_data = array();
+ }
+
+ /* Fetch more data, if available
+ *
+ * NOTE: Operator precedence is important here, if you change
+ * 'AND' with '&&' - it WILL BREAK the results, as
+ * $row will be assigned the scalar value of both
+ * expressions!
+ */
+ while ($row = $this->_fetch_assoc() AND $current_row <= $n)
+ {
+ $this->row_data[$current_row++] = $row;
+ }
+
+ // This would mean that there's no (more) data to fetch
+ if ( ! is_array($this->row_data) OR ! isset($this->row_data[$n]))
+ {
+ // Cache what we already have
+ if (is_array($this->row_data))
+ {
+ $this->num_rows = count($this->row_data);
+ /* Usually, row_data could have less elements than result_array,
+ * but at this point - they should be exactly the same.
+ */
+ $this->result_array = $this->row_data;
+ }
+ else
+ {
+ $this->num_rows = 0;
+ }
+
+ return array();
+ }
+
+ $this->current_row = $n;
+ return $this->row_data[$n];
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Single row result. Object version.
+ *
+ * @param int row index
+ * @return mixed object if row found; empty array if not
+ */
+ public function row_object($n = 0)
+ {
+ // Make sure $n is not a string
+ if ( ! is_int($n))
+ {
+ $n = (int) $n;
+ }
+ /* Logic here is exactly the same as in row_array,
+ * except we have to cast row_data[$n] to an object.
+ *
+ * If we already have result_object though - we can
+ * directly return from it.
+ */
+ if (isset($this->result_object[$n]))
+ {
+ $this->current_row = $n;
+ // Set this, if not already done.
+ if ( ! is_int($this->num_rows))
+ {
+ $this->num_rows = count($this->result_object);
+ }
+
+ return $this->result_object[$n];
+ }
+
+ $row = $this->row_array($n);
+ // Cast only if the row exists
+ if (count($row) > 0)
+ {
+ $this->current_row = $n;
+ return (object) $row;
+ }
+
+ return array();
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Single row result. Custom object version.
+ *
+ * @param int row index
+ * @param string custom class name
+ * @return mixed custom object if row found; empty array otherwise
+ */
+ public function custom_row_object($n = 0, $class_name)
+ {
+ // Make sure $n is not a string
+ if ( ! is_int($n))
+ {
+ $n = (int) $n;
+ }
+
+ if (array_key_exists($class_name, $this->custom_result_object))
+ {
+ /* We already have a the whole result set with this class_name,
+ * return the specified row if it exists, and an empty array if
+ * it doesn't.
+ */
+ if (isset($this->custom_result_object[$class_name][$n]))
+ {
+ $this->current_row = $n;
+ return $this->custom_result_object[$class_name][$n];
+ }
+ else
+ {
+ return array();
+ }
+ }
+ elseif ( ! class_exists($class_name)) // No such class exists
+ {
+ return array();
+ }
+
+ $row = $this->row_array($n);
+ // An array would mean that the row doesn't exist
+ if (is_array($row))
+ {
+ return $row;
+ }
+
+ // Convert to the desired class and return
+ $row_object = new $class_name();
+ foreach ($row as $key => $value)
+ {
+ $row_object->$key = $value;
+ }
+
+ $this->current_row = $n;
+ return $row_object;
+ }
+
+ // --------------------------------------------------------------------
+
+ /* First row result.
+ *
+ * @param string ('object', 'array' or a custom class name)
+ * @return mixed whatever was passed to the second parameter
+ */
+ public function first_row($type = 'object')
+ {
+ return $this->row(0, $type);
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Last row result.
+ *
+ * @param string ('object', 'array' or a custom class name)
+ * @return mixed whatever was passed to the second parameter
+ */
+ public function last_row($type = 'object')
+ {
+ $result = &$this->result($type);
+ if ( ! isset($this->num_rows))
+ {
+ $this->num_rows = count($result);
+ }
+ $this->current_row = $this->num_rows - 1;
+ return $result[$this->current_row];
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Next row result.
+ *
+ * @param string ('object', 'array' or a custom class name)
+ * @return mixed whatever was passed to the second parameter
+ */
+ public function next_row($type = 'object')
+ {
+ if (is_array($this->row_data))
+ {
+ $count = count($this->row_data);
+ if ($this->current_row > $count OR ($this->current_row === 0 && $count === 0))
+ {
+ $n = $count;
+ }
+ else
+ {
+ $n = $this->current_row + 1;
+ }
+ }
+ else
+ {
+ $n = 0;
+ }
+
+ return $this->row($n, $type);
+ }
+
+ // --------------------------------------------------------------------
+
+ /* Previous row result.
+ *
+ * @param string ('object', 'array' or a custom class name)
+ * @return mixed whatever was passed to the second parameter
+ */
+ public function previous_row($type = 'object')
+ {
+ $n = ($this->current_row !== 0) ? $this->current_row - 1 : 0;
+ return $this->row($n, $type);
}
// --------------------------------------------------------------------
@@ -221,16 +642,56 @@ class CI_DB_oci8_result extends CI_DB_result {
/**
* Data Seek
*
- * Moves the internal pointer to the desired offset. We call
+ * Moves the internal pointer to the desired offset. We call
* this internally before fetching results to make sure the
- * result set starts at zero
+ * result set starts at zero.
*
- * @access protected
- * @return array
+ * Oracle's PHP extension doesn't have an easy way of doing this
+ * and the only workaround is to (re)execute the statement or cursor
+ * in order to go to the first (zero) index of the result set.
+ * Then, we would need to "dummy" fetch ($n - 1) rows to get to the
+ * right one.
+ *
+ * This is as ridiculous as it sounds and it's the reason why every
+ * other method that is fetching data tries to use an already "cached"
+ * result set. Keeping this just in case it becomes needed at
+ * some point in the future, but it will only work for resetting the
+ * pointer to zero.
+ *
+ * @return bool
*/
- protected function _data_seek($n = 0)
+ protected function _data_seek()
{
- return FALSE; // Not needed
+ /* The PHP manual says that if OCI_NO_AUTO_COMMIT mode
+ * is used, and oci_rollback() and/or oci_commit() are
+ * not subsequently called - this will cause an unnecessary
+ * rollback to be triggered at the end of the script execution.
+ *
+ * Therefore we'll try to avoid using that mode flag
+ * if we're not currently in the middle of a transaction.
+ */
+ if ($this->commit_mode !== OCI_COMMIT_ON_SUCCESS)
+ {
+ $result = @oci_execute($this->stmt_id, $this->commit_mode);
+ }
+ else
+ {
+ $result = @oci_execute($this->stmt_id);
+ }
+
+ if ($result && $this->curs_id)
+ {
+ if ($this->commit_mode !== OCI_COMMIT_ON_SUCCESS)
+ {
+ return @oci_execute($this->curs_id, $this->commit_mode);
+ }
+ else
+ {
+ return @oci_execute($this->curs_id);
+ }
+ }
+
+ return $result;
}
}