Merge branch 'develop' of git://github.com/EllisLab/CodeIgniter into develop
diff --git a/application/config/migration.php b/application/config/migration.php
index dba8700..1f532f1 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -11,6 +11,35 @@
 */

 $config['migration_enabled'] = FALSE;

 

+/*

+|--------------------------------------------------------------------------

+| Migrations table

+|--------------------------------------------------------------------------

+|

+| This is the name of the table that will store the current migrations state.

+| When migrations runs it will store in a database table which migration 

+| level the system is at. It then compares the migration level in the this

+| table to the $config['migration_version'] if they are not the same it

+| will migrate up. This must be set.

+|

+*/

+$config['migration_table'] = 'migrations';

+

+

+/*

+|--------------------------------------------------------------------------

+| Auto Migrate To Latest

+|--------------------------------------------------------------------------

+|

+| If this is set to TRUE when you load the migrations class and have 

+| $config['migration_enabled'] set to TRUE the system will auto migrate

+| to your latest migration (whatever $config['migration_version'] is

+| set to). This way you do not have to call migrations anywhere else

+| in your code to have the latest migration.

+|

+*/

+$config['migration_auto_latest'] = FALSE;

+

 

 /*

 |--------------------------------------------------------------------------

diff --git a/system/core/Security.php b/system/core/Security.php
index e99418b..6c4c590 100755
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -169,6 +169,7 @@
 
 		// Nothing should last forever
 		unset($_COOKIE[$this->_csrf_cookie_name]);
+                $this->_csrf_hash = '';
 		$this->_csrf_set_hash();
 		$this->csrf_set_cookie();
 		
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index d4adfd5..1cf063e 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -79,7 +79,7 @@
 	 */
 	function db_connect()
 	{
-		return @ocilogon($this->username, $this->password, $this->hostname);
+		return @ocilogon($this->username, $this->password, $this->hostname, $this->char_set);
 	}
 
 	// --------------------------------------------------------------------
@@ -92,7 +92,7 @@
 	 */
 	function db_pconnect()
 	{
-		return @ociplogon($this->username, $this->password, $this->hostname);
+		return @ociplogon($this->username, $this->password, $this->hostname, $this->char_set);
 	}
 
 	// --------------------------------------------------------------------
@@ -136,7 +136,7 @@
 	 */
 	function db_set_charset($charset, $collation)
 	{
-		// @todo - add support if needed
+		// this is done upon connect
 		return TRUE;
 	}
 
@@ -643,6 +643,34 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Insert_batch statement
+	 *
+	 * Generates a platform-specific insert string from the supplied data
+	 *
+	 * @access      public
+	 * @param       string  the table name
+	 * @param       array   the insert keys
+	 * @param       array   the insert values
+	 * @return      string
+	 */
+	function _insert_batch($table, $keys, $values)
+	{
+		$keys = implode(', ', $keys);
+		$sql = "INSERT ALL\n";
+
+		for ($i = 0, $c = count($values); $i < $c; $i++)
+		{
+			$sql .= '	INTO ' . $table . ' (' . $keys . ') VALUES ' . $values[$i] . "\n";
+		}
+
+		$sql .= 'SELECT * FROM dual';
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
@@ -776,4 +804,4 @@
 
 
 /* End of file oci8_driver.php */
-/* Location: ./system/database/drivers/oci8/oci8_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/oci8/oci8_driver.php */
diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php
index 88531b4..2713f6f 100644
--- a/system/database/drivers/oci8/oci8_result.php
+++ b/system/database/drivers/oci8/oci8_result.php
@@ -42,15 +42,18 @@
 	 */
 	function num_rows()
 	{
-		$rowcount = count($this->result_array());
-		@ociexecute($this->stmt_id);
-
-		if ($this->curs_id)
+		if ($this->num_rows === 0 && count($this->result_array()) > 0)
 		{
-			@ociexecute($this->curs_id);
+			$this->num_rows = count($this->result_array());
+			@ociexecute($this->stmt_id);
+
+			if ($this->curs_id)
+			{
+				@ociexecute($this->curs_id);
+			}
 		}
 
-		return $rowcount;
+		return $this->num_rows;
 	}
 
 	// --------------------------------------------------------------------
@@ -246,4 +249,4 @@
 
 
 /* End of file oci8_result.php */
-/* Location: ./system/database/drivers/oci8/oci8_result.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/oci8/oci8_result.php */
diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php
index 3734e18..840cefe 100644
--- a/system/libraries/Migration.php
+++ b/system/libraries/Migration.php
@@ -32,7 +32,9 @@
 	protected $_migration_enabled = FALSE;
 	protected $_migration_path = NULL;
 	protected $_migration_version = 0;
-
+	protected $_migration_table = 'migrations';
+	protected $_migration_auto_latest = FALSE;
+	
 	protected $_error_string = '';
 
 	public function __construct($config = array())
@@ -68,16 +70,31 @@
 		// They'll probably be using dbforge
 		$this->load->dbforge();
 
+		// Make sure the migration table name was set.
+		if (empty($this->_migration_table))
+		{
+			show_error('Migrations configuration file (migration.php) must have "migration_table" set.');			
+		}
+
 		// If the migrations table is missing, make it
-		if ( ! $this->db->table_exists('migrations'))
+		if ( ! $this->db->table_exists($this->_migration_table))
 		{
 			$this->dbforge->add_field(array(
 				'version' => array('type' => 'INT', 'constraint' => 3),
 			));
 
-			$this->dbforge->create_table('migrations', TRUE);
+			$this->dbforge->create_table($this->_migration_table, TRUE);
 
-			$this->db->insert('migrations', array('version' => 0));
+			$this->db->insert($this->_migration_table, array('version' => 0));
+		}
+		
+		// Do we auto migrate to the latest migration?
+		if ( $this->_migration_auto_latest == TRUE )
+		{
+			if ( ! $this->latest() )
+			{
+				show_error($this->error_string());
+			}
 		}
 	}
 
@@ -299,7 +316,7 @@
 	 */
 	protected function _get_version()
 	{
-		$row = $this->db->get('migrations')->row();
+		$row = $this->db->get($this->_migration_table)->row();
 		return $row ? $row->version : 0;
 	}
 
@@ -314,7 +331,7 @@
 	 */
 	protected function _update_version($migrations)
 	{
-		return $this->db->update('migrations', array(
+		return $this->db->update($this->_migration_table, array(
 			'version' => $migrations
 		));
 	}
diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php
index cc62e66..eff754a 100644
--- a/system/libraries/Pagination.php
+++ b/system/libraries/Pagination.php
@@ -34,6 +34,7 @@
 	var $per_page			= 10; // Max number of items you want shown per page
 	var $num_links			=  2; // Number of "digit" links to show before/after the currently viewed page
 	var $cur_page			=  0; // The current page being viewed
+	var $use_page_numbers	= FALSE; // Use page number for segment instead of offset
 	var $first_link			= '&lsaquo; First';
 	var $next_link			= '&gt;';
 	var $prev_link			= '&lt;';
@@ -128,12 +129,15 @@
 			return '';
 		}
 
+		// Set the base page index for starting page number
+		$base_page = ($this->use_page_numbers) ? 1 : 0;
+
 		// Determine the current page number.
 		$CI =& get_instance();
 
 		if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE)
 		{
-			if ($CI->input->get($this->query_string_segment) != 0)
+			if ($CI->input->get($this->query_string_segment) != $base_page)
 			{
 				$this->cur_page = $CI->input->get($this->query_string_segment);
 
@@ -143,7 +147,7 @@
 		}
 		else
 		{
-			if ($CI->uri->segment($this->uri_segment) != 0)
+			if ($CI->uri->segment($this->uri_segment) != $base_page)
 			{
 				$this->cur_page = $CI->uri->segment($this->uri_segment);
 
@@ -151,6 +155,12 @@
 				$this->cur_page = (int) $this->cur_page;
 			}
 		}
+		
+		// Set current page to 1 if using page numbers instead of offset
+		if ($this->use_page_numbers AND $this->cur_page == 0)
+		{
+			$this->cur_page = $base_page;
+		}
 
 		$this->num_links = (int)$this->num_links;
 
@@ -161,18 +171,32 @@
 
 		if ( ! is_numeric($this->cur_page))
 		{
-			$this->cur_page = 0;
+			$this->cur_page = $base_page;
 		}
 
 		// Is the page number beyond the result range?
 		// If so we show the last page
-		if ($this->cur_page > $this->total_rows)
+		if ($this->use_page_numbers)
 		{
-			$this->cur_page = ($num_pages - 1) * $this->per_page;
+			if ($this->cur_page > $num_pages)
+			{
+				$this->cur_page = $num_pages;
+			}
+		}
+		else
+		{
+			if ($this->cur_page > $this->total_rows)
+			{
+				$this->cur_page = ($num_pages - 1) * $this->per_page;
+			}
 		}
 
 		$uri_page_number = $this->cur_page;
-		$this->cur_page = floor(($this->cur_page/$this->per_page) + 1);
+		
+		if ( ! $this->use_page_numbers)
+		{
+			$this->cur_page = floor(($this->cur_page/$this->per_page) + 1);
+		}
 
 		// Calculate the start and end numbers. These determine
 		// which number to start and end the digit links with
@@ -203,9 +227,9 @@
 		// Render the "previous" link
 		if  ($this->prev_link !== FALSE AND $this->cur_page != 1)
 		{
-			$i = $uri_page_number - $this->per_page;
+			$i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page;
 
-			if ($i == 0 && $this->first_url != '')
+			if (($i == 0 OR ($this->use_page_numbers && $i == 1)) AND $this->first_url != '')
 			{
 				$output .= $this->prev_tag_open.'<a '.$this->anchor_class.'href="'.$this->first_url.'">'.$this->prev_link.'</a>'.$this->prev_tag_close;
 			}
@@ -223,9 +247,9 @@
 			// Write the digit links
 			for ($loop = $start -1; $loop <= $end; $loop++)
 			{
-				$i = ($loop * $this->per_page) - $this->per_page;
+				$i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page;
 
-				if ($i >= 0)
+				if ($i >= $base_page)
 				{
 					if ($this->cur_page == $loop)
 					{
@@ -233,7 +257,7 @@
 					}
 					else
 					{
-						$n = ($i == 0) ? '' : $i;
+						$n = ($i == $base_page) ? '' : $i;
 
 						if ($n == '' && $this->first_url != '')
 						{
@@ -253,13 +277,16 @@
 		// Render the "next" link
 		if ($this->next_link !== FALSE AND $this->cur_page < $num_pages)
 		{
-			$output .= $this->next_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.($this->cur_page * $this->per_page).$this->suffix.'">'.$this->next_link.'</a>'.$this->next_tag_close;
+			$i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page;
+
+			$output .= $this->next_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->next_link.'</a>'.$this->next_tag_close;
 		}
 
 		// Render the "Last" link
 		if ($this->last_link !== FALSE AND ($this->cur_page + $this->num_links) < $num_pages)
 		{
-			$i = (($num_pages * $this->per_page) - $this->per_page);
+			$i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page;
+			
 			$output .= $this->last_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->last_link.'</a>'.$this->last_tag_close;
 		}
 
diff --git a/user_guide/changelog.html b/user_guide/changelog.html
index f555159..d260136 100644
--- a/user_guide/changelog.html
+++ b/user_guide/changelog.html
@@ -92,6 +92,7 @@
 			<li>
 				Added additional option 'none' for the optional third argument for  <kbd>$this->db->like()</kbd> in the <a href="database/active_record.html">Database Driver</a>.
 			</li>
+			<li>Added <kbd>$this->db->insert_batch()</kbd> support to the OCI8 (Oracle) driver.</li>
 		</ul>
 	</li>
 	<li>Libraries
@@ -104,6 +105,7 @@
 			<li><samp>CI_Loader::_ci_autoloader()</samp> is now a protected method.</li>
 			<li>Added <kbd>is_unique</kbd> to the <a href="libraries/form_validation.html">Form Validation library</a>.</li>
 			<li>Modified valid_ip() to use PHP's filter_var() when possible (>= PHP 5.2) in the <a href="libraries/form_validation.html">Form Validation</a> library.</li>
+			<li>Added <kbd>$config['use_page_numbers']</kbd> to the <a href="libraries/pagination.html">Pagination library</a>, which enables real page numbers in the URI.</li>
 		</ul>
 	</li>
 	<li>Core
@@ -129,6 +131,8 @@
 	<li>Fixed a bug (#24) - ODBC database driver called incorrect parent in __construct().</li>
 	<li>Fixed a bug (#85) - OCI8 (Oracle) database escape_str() function did not escape correct.</li>
 	<li>Fixed a bug (#344) - Using schema found in <a href="libraries/sessions.html">Saving Session Data to a Database</a>, system would throw error "user_data does not have a default value" when deleting then creating a session.</li>
+	<li>Fixed a bug (#112) - OCI8 (Oracle) driver didn't pass the configured database character set when connecting.</li>
+	<li>Fixed a bug (#182) - OCI8 (Oracle) driver used to re-execute the statement whenever num_rows() is called.</li>
 </ul>
 
 <h2>Version 2.0.3</h2>
diff --git a/user_guide/database/active_record.html b/user_guide/database/active_record.html
index 10259a4..70aecbd 100644
--- a/user_guide/database/active_record.html
+++ b/user_guide/database/active_record.html
@@ -543,7 +543,7 @@
 &nbsp;&nbsp;&nbsp;)<br />
 );<br />
 <br />
-$this->db->update_batch('mytable', $data);
+$this->db->insert_batch('mytable', $data);
 <br /><br />
 // Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'),  ('Another title', 'Another name', 'Another date')</code>
 
@@ -666,6 +666,41 @@
 
 <p>You may also use the <dfn>$this->db->set()</dfn> function described above when performing updates.</p>
 
+<h2>$this->db->update_batch();</h2>
+<p>Generates an update string based on the data you supply, and runs the query. You can either pass an
+<strong>array</strong> or an <strong>object</strong> to the function.  Here is an example using an array:</p>
+
+<code>
+$data = array(<br/>
+&nbsp;&nbsp;&nbsp;array(<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'title' => 'My title' ,<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'name' => 'My Name 2' ,<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'date' => 'My date 2'<br />
+&nbsp;&nbsp;&nbsp;),<br />
+&nbsp;&nbsp;&nbsp;array(<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'title' => 'Another title' ,<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'name' => 'Another Name 2' ,<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'date' => 'Another date 2'<br />
+&nbsp;&nbsp;&nbsp;)<br/>
+);<br />
+<br />
+$this->db->update_batch('mytable', $data, 'title');
+<br /><br />
+// Produces: <br />
+// UPDATE `mytable` SET `name` = CASE<br />
+// WHEN `title` = 'My title' THEN 'My Name 2'<br />
+// WHEN `title` = 'Another title' THEN 'Another Name 2'<br />
+// ELSE `name` END,<br />
+// `date` = CASE <br />
+// WHEN `title` = 'My title' THEN 'My date 2'<br />
+// WHEN `title` = 'Another title' THEN 'Another date 2'<br />
+// ELSE `date` END<br />
+// WHERE `title` IN ('My title','Another title')</code>
+
+<p>The first parameter will contain the table name, the second is an associative array of values, the third parameter is the where key.</p>
+
+<p class="important"><strong>Note:</strong> All values are escaped automatically producing safer queries.</p>
+
 
 <a name="delete">&nbsp;</a>
 <h1>Deleting Data</h1>
diff --git a/user_guide/database/forge.html b/user_guide/database/forge.html
index 6b87098..528d1a2 100644
--- a/user_guide/database/forge.html
+++ b/user_guide/database/forge.html
@@ -201,6 +201,10 @@
 $this-&gt;dbforge-&gt;add_column('table_name', $fields);<br />
 <br />
 // gives ALTER TABLE table_name ADD   	preferences TEXT</code></p>
+<p>An optional third parameter can be used to specify which existing column to add the new column after.</p>
+<p><code>
+$this-&gt;dbforge-&gt;add_column('table_name', $fields, 'after_field');
+</code></p>
 <h2>$this-&gt;dbforge-&gt;drop_column()</h2>
 <p>Used to remove a column from a table. </p>
 <p><code>$this-&gt;dbforge-&gt;drop_column('table_name', 'column_to_drop');</code></p>
diff --git a/user_guide/installation/upgrading.html b/user_guide/installation/upgrading.html
index 58a45ee..0f4a29b 100644
--- a/user_guide/installation/upgrading.html
+++ b/user_guide/installation/upgrading.html
@@ -60,6 +60,7 @@
 <p>Please read the upgrade notes corresponding to the version you are upgrading from.</p>
 
 <ul>
+	<li><a href="upgrade_210.html">Upgrading from 2.0.3 to 2.1.0</a></li>
 	<li><a href="upgrade_203.html">Upgrading from 2.0.2 to 2.0.3</a></li>
 	<li><a href="upgrade_202.html">Upgrading from 2.0.1 to 2.0.2</a></li>
 	<li><a href="upgrade_201.html">Upgrading from 2.0 to 2.0.1</a></li>
diff --git a/user_guide/libraries/pagination.html b/user_guide/libraries/pagination.html
index 1965554..6a14411 100644
--- a/user_guide/libraries/pagination.html
+++ b/user_guide/libraries/pagination.html
@@ -119,7 +119,11 @@
 
 <p>The number of &quot;digit&quot; links you would like before and after the selected page number. For example, the number 2
 	will place two digits on either side, as in the example links at the very top of this page.</p>
-<h4>$config['page_query_string'] = TRUE</h4>
+
+<h4>$config['use_page_numbers'] = TRUE;</h4>
+<p>By default, the URI segment will use the starting index for the items you are paginating. If you prefer to show the the actual page number, set this to TRUE.</p>
+
+<h4>$config['page_query_string'] = TRUE;</h4>
 <p>By default, the pagination library assume you are using <a href="../general/urls.html">URI Segments</a>, and constructs your links something like</p>
 <p><code>http://example.com/index.php/test/page/20</code></p>
 <p>If you have $config['enable_query_strings']  set to TRUE your links will automatically be re-written using Query Strings. This option can also be explictly set. Using $config['page_query_string'] set to TRUE, the pagination link will become.</p>