Bring back the AFTER clause for DB Forge add_column()

(it was temporarily removed due to multiple inconsistencies with other drivers)

This commit also fixes issue #1988.
Also added support for the FIRST clause (again, MySQL and CUBRID only).
diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php
index 7c9c03e..2104601 100644
--- a/system/database/DB_forge.php
+++ b/system/database/DB_forge.php
@@ -532,11 +532,13 @@
 	/**
 	 * Column Add
 	 *
+	 * @todo	Remove deprecated $_after option in 3.1+
 	 * @param	string	$table	Table name
 	 * @param	array	$field	Column definition
+	 * @param	string	$_after	Column for AFTER clause (deprecated)
 	 * @return	bool
 	 */
-	public function add_column($table = '', $field = array())
+	public function add_column($table = '', $field = array(), $_after = NULL)
 	{
 		if ($table === '')
 		{
@@ -551,6 +553,12 @@
 
 		foreach (array_keys($field) as $k)
 		{
+			// Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
+			if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
+			{
+				$field[$k]['after'] = $_after;
+			}
+
 			$this->add_field(array($k => $field[$k]));
 		}
 
diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php
index 86a41e5..05762ba 100644
--- a/system/database/drivers/cubrid/cubrid_forge.php
+++ b/system/database/drivers/cubrid/cubrid_forge.php
@@ -123,6 +123,35 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Process column
+	 *
+	 * @param	array	$field
+	 * @return	string
+	 */
+	protected function _process_column($field)
+	{
+		$extra_clause = isset($field['after'])
+			? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+		if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+		{
+			$extra_clause = ' FIRST';
+		}
+
+		return $this->db->escape_identifiers($field['name'])
+			.(empty($field['new_name']) ? '' : $this->db->escape_identifiers($field['new_name']))
+			.' '.$field['type'].$field['length']
+			.$field['unsigned']
+			.$field['null']
+			.$field['default']
+			.$field['auto_increment']
+			.$field['unique']
+			.$extra_clause;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Field attribute TYPE
 	 *
 	 * Performs a data type mapping between different databases.
diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php
index d4a8b4f..6c5dfc6 100644
--- a/system/database/drivers/mysql/mysql_forge.php
+++ b/system/database/drivers/mysql/mysql_forge.php
@@ -148,6 +148,14 @@
 	 */
 	protected function _process_column($field)
 	{
+		$extra_clause = isset($field['after'])
+			? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+		if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+		{
+			$extra_clause = ' FIRST';
+		}
+
 		return $this->db->escape_identifiers($field['name'])
 			.(empty($field['new_name']) ? '' : $this->db->escape_identifiers($field['new_name']))
 			.' '.$field['type'].$field['length']
@@ -155,7 +163,8 @@
 			.$field['null']
 			.$field['default']
 			.$field['auto_increment']
-			.$field['unique'];
+			.$field['unique']
+			.$extra_clause;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php
index 5a6560b..a70eef8 100644
--- a/system/database/drivers/mysqli/mysqli_forge.php
+++ b/system/database/drivers/mysqli/mysqli_forge.php
@@ -148,6 +148,14 @@
 	 */
 	protected function _process_column($field)
 	{
+		$extra_clause = isset($field['after'])
+			? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+		if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+		{
+			$extra_clause = ' FIRST';
+		}
+
 		return $this->db->escape_identifiers($field['name'])
 			.(empty($field['new_name']) ? '' : $this->db->escape_identifiers($field['new_name']))
 			.' '.$field['type'].$field['length']
@@ -155,7 +163,8 @@
 			.$field['null']
 			.$field['default']
 			.$field['auto_increment']
-			.$field['unique'];
+			.$field['unique']
+			.$extra_clause;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php
index d63a4b0..e8421ec 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
@@ -123,6 +123,35 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Process column
+	 *
+	 * @param	array	$field
+	 * @return	string
+	 */
+	protected function _process_column($field)
+	{
+		$extra_clause = isset($field['after'])
+			? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+		if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+		{
+			$extra_clause = ' FIRST';
+		}
+
+		return $this->db->escape_identifiers($field['name'])
+			.(empty($field['new_name']) ? '' : $this->db->escape_identifiers($field['new_name']))
+			.' '.$field['type'].$field['length']
+			.$field['unsigned']
+			.$field['null']
+			.$field['default']
+			.$field['auto_increment']
+			.$field['unique']
+			.$extra_clause;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Field attribute TYPE
 	 *
 	 * Performs a data type mapping between different databases.
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php
index fb33127..236e1cc 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php
index 88c5351..474188f 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php b/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php
index 6a2c9b8..f095c71 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php b/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php
index 715a7cb..8722dd2 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php
index 24c470a..866b6fb 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
@@ -162,6 +162,14 @@
 	 */
 	protected function _process_column($field)
 	{
+		$extra_clause = isset($field['after'])
+			? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+		if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+		{
+			$extra_clause = ' FIRST';
+		}
+
 		return $this->db->escape_identifiers($field['name'])
 			.(empty($field['new_name']) ? '' : $this->db->escape_identifiers($field['new_name']))
 			.' '.$field['type'].$field['length']
@@ -169,7 +177,8 @@
 			.$field['null']
 			.$field['default']
 			.$field['auto_increment']
-			.$field['unique'];
+			.$field['unique']
+			.$extra_clause;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php b/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php
index dbcb64d..8052d21 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php
index cd6b19c..ab4d2bf 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php
index 1e4d2ac..484c160 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
index 2e5f123..da098df 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php
index c62720d..e4b669f 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php
@@ -1,4 +1,4 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
 /**
  * CodeIgniter
  *
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index f3509d0..792969c 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -168,7 +168,8 @@
    -  :doc:`Database Forge <database/forge>` changes include:
 	 - Added an optional second parameter to ``drop_table()`` that allows adding the **IF EXISTS** condition, which is no longer the default.
 	 - Added support for passing a custom database object to the loader.
-	 - Removed the optional AFTER clause from method ``add_column()``.
+	 - Deprecated ``add_column()``'s third method. *AFTER* clause should now be added to the field definition array instead.
+	 - Added support for usage of the *FIRST* clause in ``add_column()`` for MySQL and CUBRID.
 	 - Overall improved support for all of the drivers.
    -  :doc:`Database Utility <database/utilities>` chages include:
 	 - Added support for passing a custom database object to the loader.
diff --git a/user_guide_src/source/database/forge.rst b/user_guide_src/source/database/forge.rst
index de5748b..ca904ed 100644
--- a/user_guide_src/source/database/forge.rst
+++ b/user_guide_src/source/database/forge.rst
@@ -231,7 +231,7 @@
 ****************
 
 $this->dbforge->add_column()
-=============================
+============================
 
 The ``add_column()`` method is used to modify an existing table. It
 accepts the same field array as above, and can be used for an unlimited
@@ -243,10 +243,25 @@
 		'preferences' => array('type' => 'TEXT')
 	);
 	$this->dbforge->add_column('table_name', $fields); 
-	// gives ALTER TABLE table_name ADD preferences TEXT
+	// Executes: ALTER TABLE table_name ADD preferences TEXT
+
+If you are using MySQL or CUBIRD, then you can take advantage of their
+AFTER and FIRST clauses to position the new column.
+
+Examples::
+
+	// Will place the new column after the `another_field` column:
+	$fields = array(
+		'preferences' => array('type' => 'TEXT', 'after' => 'another_field')
+	);
+
+	// Will place the new column at the start of the table definition:
+	$fields = array(
+		'preferences' => array('type' => 'TEXT', 'first' => TRUE)
+	);
 
 $this->dbforge->drop_column()
-==============================
+=============================
 
 Used to remove a column from a table.
 
@@ -256,7 +271,7 @@
 
 
 $this->dbforge->modify_column()
-================================
+===============================
 
 The usage of this method is identical to ``add_column()``, except it
 alters an existing column rather than adding a new one. In order to
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 2bfb1bd..0af21b1 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -284,4 +284,34 @@
 in CodeIgniter 3.1+.
 
 .. note:: These options are still available, but you're strongly encouraged to remove their usage
-	sooner rather than later.
\ No newline at end of file
+	sooner rather than later.
+
+Database Forge method add_column() with an AFTER clause
+=======================================================
+
+If you have used the **third parameter** for :doc:`Database Forge <database/forge>` method
+``add_column()`` to add a field for an AFTER clause, then you should change its usage.
+
+That third parameter has been deprecated and scheduled for removal in CodeIgniter 3.1+.
+
+You should now put AFTER clause field names in the field definition array instead::
+
+	// Old usage:
+	$field = array(
+		'new_field' => array('type' => 'TEXT')
+	);
+
+	$this->dbforge->add_column('table_name', $field, 'another_field');
+
+	// New usage:
+	$field = array(
+		'new_field' => array('type' => 'TEXT', 'after' => 'another_field')
+	);
+
+	$this->dbforge->add_column('table_name', $field);
+
+.. note:: The parameter is still available, but you're strongly encouraged to remove its usage
+	sooner rather than later.
+
+.. note:: This is for MySQL and CUBRID databases only! Other drivers don't support this
+	clause and will silently ignore it.