<?php
/** @file
 * This package contains classes for handling database transactions from
 * within QueryPath.
 *
 * The tools here use the PDO (PHP Data Objects) library to execute database
 * functions.
 *
 * Using tools in this package, you can write QueryPath database queries
 * that query an RDBMS and then insert the results into the document.
 *
 * Example:
 *
 * @code
 * <?php
 * $template = '<?xml version="1.0"?><tr><td class="colOne"/><td class="colTwo"/><td class="colThree"/></tr>';
 * $qp = qp(QueryPath::HTML_STUB, 'body') // Open a stub HTML doc and select <body/>
 *  ->append('<table><tbody/></table>')
 *  ->dbInit($this->dsn)
 *  ->queryInto('SELECT * FROM qpdb_test WHERE 1', array(), $template)
 *  ->doneWithQuery()
 *  ->writeHTML();
 * ?>
 * @endcode
 *
 * The code above will take the results of a SQL query and insert them into a n
 * HTML table.
 *
 * If you are doing many database operations across multiple QueryPath objects,
 * it is better to avoid using {@link QPDB::dbInit()}. Instead, you should
 * call the static {@link QPDB::baseDB()} method to configure a single database
 * connection that can be shared by all {@link QueryPath} instances.
 *
 * Thus, we could rewrite the above to look like this:
 * @code
 * <?php
 * QPDB::baseDB($someDN);
 *
 * $template = '<?xml version="1.0"?><tr><td class="colOne"/><td class="colTwo"/><td class="colThree"/></tr>';
 * $qp = qp(QueryPath::HTML_STUB, 'body') // Open a stub HTML doc and select <body/>
 *  ->append('<table><tbody/></table>')
 *  ->queryInto('SELECT * FROM qpdb_test WHERE 1', array(), $template)
 *  ->doneWithQuery()
 *  ->writeHTML();
 * ?>
 * @endcode
 *
 * Note that in this case, the QueryPath object doesn't need to call a method to
 * activate the database. There is no call to {@link dbInit()}. Instead, it checks
 * the base class to find the shared database.
 *
 * (Note that if you were to add a dbInit() call to the above, it would create
 * a new database connection.)
 *
 * The result of both of these examples will be identical.
 * The output looks something like this:
 *
 * @code
 * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 * <html xmlns="http://www.w3.org/1999/xhtml">
 * <head>
 *  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
 * 	<title>Untitled</title>
 * </head>
 *<body>
 *<table>
 * <tbody>
 *  <tr>
 *   <td class="colOne">Title 0</td>
 *   <td class="colTwo">Body 0</td>
 *   <td class="colThree">Footer 0</td>
 *  </tr>
 *  <tr>
 *   <td class="colOne">Title 1</td>
 *   <td class="colTwo">Body 1</td>
 *   <td class="colThree">Footer 1</td>
 *  </tr>
 *  <tr>
 *   <td class="colOne">Title 2</td>
 *   <td class="colTwo">Body 2</td>
 *   <td class="colThree">Footer 2</td>
 *  </tr>
 *  <tr>
 *   <td class="colOne">Title 3</td>
 *   <td class="colTwo">Body 3</td>
 *   <td class="colThree">Footer 3</td>
 *  </tr>
 *  <tr>
 *   <td class="colOne">Title 4</td>
 *   <td class="colTwo">Body 4</td>
 *   <td class="colThree">Footer 4</td>
 *  </tr>
 * </tbody>
 *</table>
 *</body>
 *</html>
 * @endcode
 *
 * Note how the CSS classes are used to correlate DB table names to template
 * locations.
 *
 *
 * @author M Butcher <matt@aleph-null.tv>
 * @license http://opensource.org/licenses/lgpl-2.1.php LGPL or MIT-like license.
 * @see QueryPathExtension
 * @see QueryPathExtensionRegistry::extend()
 * @see QPDB
 */

/**
 * Provide DB access to a QueryPath object.
 *
 * This extension provides tools for communicating with a database using the
 * QueryPath library. It relies upon PDO for underlying database communiction. This
 * means that it supports all databases that PDO supports, including MySQL,
 * PostgreSQL, and SQLite.
 *
 * Here is an extended example taken from the unit tests for this library.
 *
 * Let's say we create a database with code like this:
 * @code
 *<?php
 * public function setUp() {
 *   $this->db = new PDO($this->dsn);
 *   $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 *   $this->db->exec('CREATE TABLE IF NOT EXISTS qpdb_test (colOne, colTwo, colThree)');
 *
 *   $stmt = $this->db->prepare(
 *     'INSERT INTO qpdb_test (colOne, colTwo, colThree) VALUES (:one, :two, :three)'
 *   );
 *
 *   for ($i = 0; $i < 5; ++$i) {
 *     $vals = array(':one' => 'Title ' . $i, ':two' => 'Body ' . $i, ':three' => 'Footer ' . $i);
 *     $stmt->execute($vals);
 *     $stmt->closeCursor();
 *   }
 * }
 * ?>
 * @endcode
 *
 * From QueryPath with QPDB, we can now do very elaborate DB chains like this:
 *
 * @code
 * <?php
 * $sql = 'SELECT * FROM qpdb_test';
 * $args = array();
 * $qp = qp(QueryPath::HTML_STUB, 'body') // Open a stub HTML doc and select <body/>
 *   ->append('<h1></h1>') // Add <h1/>
 *   ->children()  // Select the <h1/>
 *   ->dbInit($this->dsn) // Connect to the database
 *   ->query($sql, $args) // Execute the SQL query
 *   ->nextRow()  // Select a row. By default, no row is selected.
 *   ->appendColumn('colOne') // Append Row 1, Col 1 (Title 0)
 *   ->parent() // Go back to the <body/>
 *   ->append('<p/>') // Append a <p/> to the body
 *   ->find('p')  // Find the <p/> we just created.
 *   ->nextRow() // Advance to row 2
 *   ->prependColumn('colTwo') // Get row 2, col 2. (Body 1)
 *   ->columnAfter('colThree') // Get row 2 col 3. (Footer 1)
 *   ->doneWithQuery() // Let QueryPath clean up. YOU SHOULD ALWAYS DO THIS.
 *   ->writeHTML(); // Write the output as HTML.
 * ?>
 * @endcode
 *
 * With the code above, we step through the document, selectively building elements
 * as we go, and then populating this elements with data from our initial query.
 *
 * When the last command, {@link QueryPath:::writeHTML()}, is run, we will get output
 * like this:
 *
 * @code
 *   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 *   <html xmlns="http://www.w3.org/1999/xhtml">
 *     <head>
 *     	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 *     	<title>Untitled</title>
 *     </head>
 *     <body>
 *       <h1>Title 0</h1>
 *       <p>Body 1</p>
 *       Footer 1</body>
 *    </html>
 * @endcode
 *
 * Notice the body section in particular. This is where the data has been
 * inserted.
 *
 * Sometimes you want to do something a lot simpler, like give QueryPath a
 * template and have it navigate a query, inserting the data into a template, and
 * then inserting the template into the document. This can be done simply with
 * the {@link queryInto()} function.
 *
 * Here's an example from another unit test:
 *
 * @code
 * <?php
 * $template = '<?xml version="1.0"?><li class="colOne"/>';
 * $sql = 'SELECT * FROM qpdb_test';
 * $args = array();
 * $qp = qp(QueryPath::HTML_STUB, 'body')
 *   ->append('<ul/>') // Add a new <ul/>
 *   ->children() // Select the <ul/>
 *   ->dbInit($this->dsn) // Initialize the DB
 *   // BIG LINE: Query the results, run them through the template, and insert them.
 *   ->queryInto($sql, $args, $template)
 *   ->doneWithQuery()
 *   ->writeHTML(); // Write the results as HTML.
 * ?>
 * @endcode
 *
 * The simple code above puts the first column of the select statement
 * into an unordered list. The example output looks like this:
 *
 * @code
 * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 * <html xmlns="http://www.w3.org/1999/xhtml">
 *   <head>
 *   	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
 *   	<title>Untitled</title>
 *    </head>
 *    <body>
 *    <ul>
 *    <li class="colOne">Title 0</li>
 *    <li class="colOne">Title 1</li>
 *    <li class="colOne">Title 2</li>
 *    <li class="colOne">Title 3</li>
 *    <li class="colOne">Title 4</li>
 *    </ul>
 *   </body>
 * </html>
 * @endcode
 *
 * Typical starting methods for this class are {@link QPDB::baseDB()},
 * {@link QPDB::query()}, and {@link QPDB::queryInto()}.
 *
 * @ingroup querypath_extensions
 */
class QPDB implements QueryPathExtension {
	protected $qp;
	protected $dsn;
	protected $db;
	protected $opts;
	protected $row = NULL;
	protected $stmt = NULL;

	protected static $con = NULL;

	/**
	 * Create a new database instance for all QueryPath objects to share.
	 *
	 * This method need be called only once. From there, other QPDB instances
	 * will (by default) share the same database instance.
	 *
	 * Normally, a DSN should be passed in. Username, password, and db params
	 * are all passed in using the options array.
	 *
	 * On rare occasions, it may be more fitting to pass in an existing database
	 * connection (which must be a {@link PDO} object). In such cases, the $dsn
	 * parameter can take a PDO object instead of a DSN string. The standard options
	 * will be ignored, though.
	 *
	 * <b>Warning:</b> If you pass in a PDO object that is configured to NOT throw
	 * exceptions, you will need to handle error checking differently.
	 *
	 * <b>Remember to always use {@link QPDB::doneWithQuery()} when you are done
	 * with a query. It gives PDO a chance to clean up open connections that may
	 * prevent other instances from accessing or modifying data.</b>
	 *
	 * @param string $dsn
	 *  The DSN of the database to connect to. You can also pass in a PDO object, which
	 *  will set the QPDB object's database to the one passed in.
	 * @param array $options
	 *  An array of configuration options. The following options are currently supported:
	 *  - username => (string)
	 *  - password => (string)
	 *  - db params => (array) These will be passed into the new PDO object.
	 *    See the PDO documentation for a list of options. By default, the
	 *    only flag set is {@link PDO::ATTR_ERRMODE}, which is set to
	 *    {@link PDO::ERRMODE_EXCEPTION}.
	 * @throws PDOException
	 *  An exception may be thrown if the connection cannot be made.
	 */
	static function baseDB($dsn, $options = array()) {

		$opts = $options + array(
      'username' => NULL,
      'password' => NULL,
      'db params' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
		);

		// Allow this to handle the case where an outside
		// connection does the initialization.
		if ($dsn instanceof PDO) {
			self::$con = $dsn;
			return;
		}
		self::$con = new PDO($dsn, $opts['username'], $opts['password'], $opts['db params']);
	}

	/**
	 *
	 * This method may be used to share the connection with other,
	 * non-QueryPath objects.
	 */
	static function getBaseDB() {return self::$con;}

	/**
	 * Used to control whether or not all rows in a result should be cycled through.
	 */
	protected $cycleRows = FALSE;

	/**
	 * Construct a new QPDB object. This is usually done by QueryPath itself.
	 */
	public function __construct(QueryPath $qp) {
		$this->qp = $qp;
		// By default, we set it up to use the base DB.
		$this->db = self::$con;
	}

	/**
	 * Create a new connection to the database. Use the PDO DSN syntax for a
	 * connection string.
	 *
	 * This creates a database connection that will last for the duration of
	 * the QueryPath object. This method ought to be used only in two cases:
	 * - When you will only run a couple of queries during the life of the
	 *   process.
	 * - When you need to connect to a database that will only be used for
	 *   a few things.
	 * Otherwise, you should use {@link QPDB::baseDB} to configure a single
	 * database connection that all of {@link QueryPath} can share.
	 *
	 * <b>Remember to always use {@link QPDB::doneWithQuery()} when you are done
	 * with a query. It gives PDO a chance to clean up open connections that may
	 * prevent other instances from accessing or modifying data.</b>
	 *
	 * @param string $dsn
	 *  The PDO DSN connection string.
	 * @param array $options
	 *  Connection options. The following options are supported:
	 *  - username => (string)
	 *  - password => (string)
	 *  - db params => (array) These will be passed into the new PDO object.
	 *    See the PDO documentation for a list of options. By default, the
	 *    only flag set is {@link PDO::ATTR_ERRMODE}, which is set to
	 *    {@link PDO::ERRMODE_EXCEPTION}.
	 * @return QueryPath
	 *  The QueryPath object.
	 * @throws PDOException
	 *  The PDO library is configured to throw exceptions, so any of the
	 *  database functions may throw a PDOException.
	 */
	public function dbInit($dsn, $options = array()) {
		$this->opts = $options + array(
      'username' => NULL,
      'password' => NULL,
      'db params' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
		);
		$this->dsn = $dsn;
		$this->db = new PDO($dsn, $this->opts['username'], $this->opts['password'], $this->opts['db params']);
		/*
		 foreach ($this->opts['db params'] as $key => $val)
		 $this->db->setAttribute($key, $val);
		 */

		return $this->qp;
	}

	/**
	 * Execute a SQL query, and store the results.
	 *
	 * This will execute a SQL query (as a prepared statement), and then store
	 * the results internally for later use. The data can be iterated using
	 * {@link nextRow()}. QueryPath can also be instructed to do internal iteration
	 * using the {@link withEachRow()} method. Finally, on the occasion that the
	 * statement itself is needed, {@link getStatement()} can be used.
	 *
	 * Use this when you need to access the results of a query, or when the
	 * parameter to a query should be escaped. If the query takes no external
	 * parameters and does not return results, you may wish to use the
	 * (ever so slightly faster) {@link exec()} function instead.
	 *
	 * Make sure you use {@link doneWithQuery()} after finishing with the database
	 * results returned by this method.
	 *
	 * <b>Usage</b>
	 *
	 * Here is a simple example:
	 * <code>
	 * <?php
	 * QPQDB::baseDB($someDSN);
	 *
	 * $args = array(':something' => 'myColumn');
	 * qp()->query('SELECT :something FROM foo', $args)->doneWithQuery();
	 * ?>
	 * </code>
	 *
	 * The above would execute the given query, substituting myColumn in place of
	 * :something before executing the query The {@link doneWithQuery()} method
	 * indicates that we are not going to use the results for anything. This method
	 * discards the results.
	 *
	 * A more typical use of the query() function would involve inserting data
	 * using {@link appendColumn()}, {@link prependColumn()}, {@link columnBefore()},
	 * or {@link columnAfter()}. See the main documentation for {@link QPDB} to view
	 * a more realistic example.
	 *
	 * @param string $sql
	 *  The query to be executed.
	 * @param array $args
	 *  An associative array of substitutions to make.
	 * @throws PDOException
	 *  Throws an exception if the query cannot be executed.
	 */
	public function query($sql, $args = array()) {
		$this->stmt = $this->db->prepare($sql);
		$this->stmt->execute($args);
		return $this->qp;
	}

	/**
	 * Query and append the results.
	 *
	 * Run a query and inject the results directly into the
	 * elements in the QueryPath object.
	 *
	 * If the third argument is empty, the data will be inserted directly into
	 * the QueryPath elements unaltered. However, if a template is provided in
	 * the third parameter, the query data will be merged into that template
	 * and then be added to each QueryPath element.
	 *
	 * The template will be merged once for each row, even if no row data is
	 * appended into the template.
	 *
	 * A template is simply a piece of markup labeled for insertion of
	 * data. See {@link QPTPL} and {@link QPTPL.php} for more information.
	 *
	 * Since this does not use a stanard {@link query()}, there is no need
	 * to call {@link doneWithQuery()} after this method.
	 *
	 * @param string $sql
	 *  The SQL query to execute. In this context, the query is typically a
	 *  SELECT statement.
	 * @param array $args
	 *  An array of arguments to be substituted into the query. See {@link query()}
	 *  for details.
	 * @param mixed $template
	 *  A template into which query results will be merged prior to being appended
	 *  into the QueryPath. For details on the template, see {@link QPTPL::tpl()}.
	 * @see QPTPL.php
	 * @see QPTPL::tpl()
	 * @see query()
	 */
	public function queryInto($sql, $args = array(), $template = NULL) {
		$stmt = $this->db->prepare($sql);
		$stmt->setFetchMode(PDO::FETCH_ASSOC);
		$stmt->execute($args);

		// If no template, put all values in together.
		if (empty($template)) {
			foreach ($stmt as $row) foreach ($row as $datum) $this->qp->append($datum);
		}
		// Otherwise, we run the results through a template, and then append.
		else {
			foreach ($stmt as $row) $this->qp->tpl($template, $row);
		}

		$stmt->closeCursor();
		return $this->qp;
	}

	/**
	 * Free up resources when a query is no longer used.
	 *
	 * This function should <i>always</i> be called when the database
	 * results for a query are no longer needed. This frees up the
	 * database cursor, discards the data, and resets resources for future
	 * use.
	 *
	 * If this method is not called, some PDO database drivers will not allow
	 * subsequent queries, while others will keep tables in a locked state where
	 * writes will not be allowed.
	 *
	 * @return QueryPath
	 *  The QueryPath object.
	 */
	public function doneWithQuery() {
		if (isset($this->stmt) && $this->stmt instanceof PDOStatement) {
			// Some drivers choke if results haven't been iterated.
			//while($this->stmt->fetch()) {}
			$this->stmt->closeCursor();
		}

		unset($this->stmt);
		$this->row = NULL;
		$this->cycleRows = FALSE;
		return $this->qp;
	}

	/**
	 * Execute a SQL query, but expect no value.
	 *
	 * If your SQL query will have parameters, you are encouraged to
	 * use {@link query()}, which includes built-in SQL Injection
	 * protection.
	 *
	 * @param string $sql
	 *  A SQL statement.
	 * @throws PDOException
	 *  An exception will be thrown if a query cannot be executed.
	 */
	public function exec($sql) {
		$this->db->exec($sql);
		return $this->qp;
	}

	/**
	 * Advance the query results row cursor.
	 *
	 * In a result set where more than one row was returned, this will
	 * move the pointer to the next row in the set.
	 *
	 * The PDO library does not have a consistent way of determining how many
	 * rows a result set has. The suggested technique is to first execute a
	 * COUNT() SQL query and get the data from that.
	 *
	 * The {@link withEachRow()} method will begin at the next row after the
	 * currently selected one.
	 *
	 * @return QueryPath
	 *  The QueryPath object.
	 */
	public function nextRow() {
		$this->row = $this->stmt->fetch(PDO::FETCH_ASSOC);
		return $this->qp;
	}

	/**
	 * Set the object to use each row, instead of only one row.
	 *
	 * This is used primarily to instruct QPDB to iterate through all of the
	 * rows when appending, prepending, inserting before, or inserting after.
	 *
	 * @return QueryPath
	 *  The QueryPath object.
	 * @see appendColumn()
	 * @see prependColumn()
	 * @see columnBefore()
	 * @see columnAfter()
	 */
	public function withEachRow() {
		$this->cycleRows = TRUE;
		return $this->qp;
	}

	/**
	 * This is the implementation behind the append/prepend and before/after methods.
	 *
	 * @param mixed $columnName
	 *  The name of the column whose data should be added to the currently selected
	 *  elements. This can be either a string or an array of strings.
	 * @param string $qpFunc
	 *  The name of the QueryPath function that should be executed to insert data
	 *  into the object.
	 * @param string $wrap
	 *  The HTML/XML markup that will be used to wrap around the column data before
	 *  the data is inserted into the QueryPath object.
	 */
	protected function addData($columnName, $qpFunc = 'append', $wrap = NULL) {
		$columns = is_array($columnName) ? $columnName : array($columnName);
		$hasWrap = !empty($wrap);
		if ($this->cycleRows) {
			while (($row = $this->stmt->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
				foreach ($columns as $col) {
					if (isset($row[$col])) {
						$data = $row[$col];
						if ($hasWrap)
						$data = qp()->append($wrap)->deepest()->append($data)->top();
						$this->qp->$qpFunc($data);
					}
				}
			}
			$this->cycleRows = FALSE;
			$this->doneWithQuery();
		}
		else {
			if ($this->row !== FALSE) {
				foreach ($columns as $col) {
					if (isset($this->row[$col])) {
						$data = $this->row[$col];
						if ($hasWrap)
						$data = qp()->append($wrap)->deepest()->append($data)->top();
						$this->qp->$qpFunc($data);
					}
				}
			}
		}
		return $this->qp;
	}

	/**
	 * Get back the raw PDOStatement object after a {@link query()}.
	 *
	 * @return PDOStatement
	 *  Return the PDO statement object. If this is called and no statement
	 *  has been executed (or the statement has already been cleaned up),
	 *  this will return NULL.
	 */
	public function getStatement() {
		return $this->stmt;
	}

	/**
	 * Get the last insert ID.
	 *
	 * This will only return a meaningful result when used after an INSERT.
	 *
	 * @return mixed
	 *  Return the ID from the last insert. The value and behavior of this
	 *  is database-dependent. See the official PDO driver documentation for
	 *  the database you are using.
	 * @since 1.3
	 */
	public function getLastInsertID() {
		$con = self::$con;
		return $con->lastInsertId();
	}

	/**
	 * Append the data in the given column(s) to the QueryPath.
	 *
	 * This appends data to every item in the current QueryPath. The data will
	 * be retrieved from the database result, using $columnName as the key.
	 *
	 * @param mixed $columnName
	 *  Either a string or an array of strings. The value(s) here should match
	 *  one or more column headers from the current SQL {@link query}'s results.
	 * @param string $wrap
	 *  IF this is supplied, then the value or values retrieved from the database
	 *  will be wrapped in this HTML/XML before being inserted into the QueryPath.
	 * @see QueryPath::wrap()
	 * @see QueryPath::append()
	 */
	public function appendColumn($columnName, $wrap = NULL) {
		return $this->addData($columnName, 'append', $wrap);
	}

	/**
	 * Prepend the data from the given column into the QueryPath.
	 *
	 * This takes the data from the given column(s) and inserts it into each
	 * element currently found in the QueryPath.
	 * @param mixed $columnName
	 *  Either a string or an array of strings. The value(s) here should match
	 *  one or more column headers from the current SQL {@link query}'s results.
	 * @param string $wrap
	 *  IF this is supplied, then the value or values retrieved from the database
	 *  will be wrapped in this HTML/XML before being inserted into the QueryPath.
	 * @see QueryPath::wrap()
	 * @see QueryPath::prepend()
	 */
	public function prependColumn($columnName, $wrap = NULL) {
		return $this->addData($columnName, 'prepend', $wrap);
	}

	/**
	 * Insert the data from the given column before each element in the QueryPath.
	 *
	 * This inserts the data before each element in the currently matched QueryPath.
	 *
	 * @param mixed $columnName
	 *  Either a string or an array of strings. The value(s) here should match
	 *  one or more column headers from the current SQL {@link query}'s results.
	 * @param string $wrap
	 *  IF this is supplied, then the value or values retrieved from the database
	 *  will be wrapped in this HTML/XML before being inserted into the QueryPath.
	 * @see QueryPath::wrap()
	 * @see QueryPath::before()
	 * @see prependColumn()
	 */
	public function columnBefore($columnName, $wrap = NULL) {
		return $this->addData($columnName, 'before', $wrap);
	}

	/**
	 * Insert data from the given column(s) after each element in the QueryPath.
	 *
	 * This inserts data from the given columns after each element in the QueryPath
	 * object. IF HTML/XML is given in the $wrap parameter, then the column data
	 * will be wrapped in that markup before being inserted into the QueryPath.
	 *
	 * @param mixed $columnName
	 *  Either a string or an array of strings. The value(s) here should match
	 *  one or more column headers from the current SQL {@link query}'s results.
	 * @param string $wrap
	 *  IF this is supplied, then the value or values retrieved from the database
	 *  will be wrapped in this HTML/XML before being inserted into the QueryPath.
	 * @see QueryPath::wrap()
	 * @see QueryPath::after()
	 * @see appendColumn()
	 */
	public function columnAfter($columnName, $wrap = NULL) {
		return $this->addData($columnName, 'after', $wrap);
	}

}

// The define allows another class to extend this.
if (!defined('QPDB_OVERRIDE'))
QueryPathExtensionRegistry::extend('QPDB');