summaryrefslogtreecommitdiff
path: root/_darcs/pristine/extlib/DB
diff options
context:
space:
mode:
Diffstat (limited to '_darcs/pristine/extlib/DB')
-rw-r--r--_darcs/pristine/extlib/DB/DataObject.php4152
-rw-r--r--_darcs/pristine/extlib/DB/DataObject/Cast.php546
-rw-r--r--_darcs/pristine/extlib/DB/DataObject/Error.php53
-rw-r--r--_darcs/pristine/extlib/DB/DataObject/Generator.php1553
-rw-r--r--_darcs/pristine/extlib/DB/DataObject/createTables.php59
-rw-r--r--_darcs/pristine/extlib/DB/common.php2262
-rw-r--r--_darcs/pristine/extlib/DB/dbase.php510
-rw-r--r--_darcs/pristine/extlib/DB/fbsql.php769
-rw-r--r--_darcs/pristine/extlib/DB/ibase.php1082
-rw-r--r--_darcs/pristine/extlib/DB/ifx.php683
-rw-r--r--_darcs/pristine/extlib/DB/msql.php831
-rw-r--r--_darcs/pristine/extlib/DB/mssql.php963
-rw-r--r--_darcs/pristine/extlib/DB/mysql.php1045
-rw-r--r--_darcs/pristine/extlib/DB/mysqli.php1092
-rw-r--r--_darcs/pristine/extlib/DB/oci8.php1156
-rw-r--r--_darcs/pristine/extlib/DB/odbc.php883
-rw-r--r--_darcs/pristine/extlib/DB/pgsql.php1135
-rw-r--r--_darcs/pristine/extlib/DB/sqlite.php960
-rw-r--r--_darcs/pristine/extlib/DB/storage.php506
-rw-r--r--_darcs/pristine/extlib/DB/sybase.php942
20 files changed, 21182 insertions, 0 deletions
diff --git a/_darcs/pristine/extlib/DB/DataObject.php b/_darcs/pristine/extlib/DB/DataObject.php
new file mode 100644
index 000000000..b1a1a4e21
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/DataObject.php
@@ -0,0 +1,4152 @@
+<?php
+/**
+ * Object Based Database Query Builder and data store
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB_DataObject
+ * @author Alan Knowles <alan@akbkhome.com>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: DataObject.php,v 1.439 2008/01/30 02:14:06 alan_k Exp $
+ * @link http://pear.php.net/package/DB_DataObject
+ */
+
+
+/* ===========================================================================
+ *
+ * !!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!
+ *
+ * THIS MAY SEGFAULT PHP IF YOU ARE USING THE ZEND OPTIMIZER (to fix it,
+ * just add "define('DB_DATAOBJECT_NO_OVERLOAD',true);" before you include
+ * this file. reducing the optimization level may also solve the segfault.
+ * ===========================================================================
+ */
+
+/**
+ * The main "DB_DataObject" class is really a base class for your own tables classes
+ *
+ * // Set up the class by creating an ini file (refer to the manual for more details
+ * [DB_DataObject]
+ * database = mysql:/username:password@host/database
+ * schema_location = /home/myapplication/database
+ * class_location = /home/myapplication/DBTables/
+ * clase_prefix = DBTables_
+ *
+ *
+ * //Start and initialize...................... - dont forget the &
+ * $config = parse_ini_file('example.ini',true);
+ * $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ * $options = $config['DB_DataObject'];
+ *
+ * // example of a class (that does not use the 'auto generated tables data')
+ * class mytable extends DB_DataObject {
+ * // mandatory - set the table
+ * var $_database_dsn = "mysql://username:password@localhost/database";
+ * var $__table = "mytable";
+ * function table() {
+ * return array(
+ * 'id' => 1, // integer or number
+ * 'name' => 2, // string
+ * );
+ * }
+ * function keys() {
+ * return array('id');
+ * }
+ * }
+ *
+ * // use in the application
+ *
+ *
+ * Simple get one row
+ *
+ * $instance = new mytable;
+ * $instance->get("id",12);
+ * echo $instance->somedata;
+ *
+ *
+ * Get multiple rows
+ *
+ * $instance = new mytable;
+ * $instance->whereAdd("ID > 12");
+ * $instance->whereAdd("ID < 14");
+ * $instance->find();
+ * while ($instance->fetch()) {
+ * echo $instance->somedata;
+ * }
+
+
+/**
+ * Needed classes
+ * - we use getStaticProperty from PEAR pretty extensively (cant remove it ATM)
+ */
+
+require_once 'PEAR.php';
+
+/**
+ * We are duping fetchmode constants to be compatible with
+ * both DB and MDB2
+ */
+define('DB_DATAOBJECT_FETCHMODE_ORDERED',1);
+define('DB_DATAOBJECT_FETCHMODE_ASSOC',2);
+
+
+
+
+
+/**
+ * these are constants for the get_table array
+ * user to determine what type of escaping is required around the object vars.
+ */
+define('DB_DATAOBJECT_INT', 1); // does not require ''
+define('DB_DATAOBJECT_STR', 2); // requires ''
+
+define('DB_DATAOBJECT_DATE', 4); // is date #TODO
+define('DB_DATAOBJECT_TIME', 8); // is time #TODO
+define('DB_DATAOBJECT_BOOL', 16); // is boolean #TODO
+define('DB_DATAOBJECT_TXT', 32); // is long text #TODO
+define('DB_DATAOBJECT_BLOB', 64); // is blob type
+
+
+define('DB_DATAOBJECT_NOTNULL', 128); // not null col.
+define('DB_DATAOBJECT_MYSQLTIMESTAMP' , 256); // mysql timestamps (ignored by update/insert)
+/*
+ * Define this before you include DataObjects.php to disable overload - if it segfaults due to Zend optimizer..
+ */
+//define('DB_DATAOBJECT_NO_OVERLOAD',true)
+
+
+/**
+ * Theses are the standard error codes, most methods will fail silently - and return false
+ * to access the error message either use $table->_lastError
+ * or $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
+ * the code is $last_error->code, and the message is $last_error->message (a standard PEAR error)
+ */
+
+define('DB_DATAOBJECT_ERROR_INVALIDARGS', -1); // wrong args to function
+define('DB_DATAOBJECT_ERROR_NODATA', -2); // no data available
+define('DB_DATAOBJECT_ERROR_INVALIDCONFIG', -3); // something wrong with the config
+define('DB_DATAOBJECT_ERROR_NOCLASS', -4); // no class exists
+define('DB_DATAOBJECT_ERROR_INVALID_CALL' ,-7); // overlad getter/setter failure
+
+/**
+ * Used in methods like delete() and count() to specify that the method should
+ * build the condition only out of the whereAdd's and not the object parameters.
+ */
+define('DB_DATAOBJECT_WHEREADD_ONLY', true);
+
+/**
+ *
+ * storage for connection and result objects,
+ * it is done this way so that print_r()'ing the is smaller, and
+ * it reduces the memory size of the object.
+ * -- future versions may use $this->_connection = & PEAR object..
+ * although will need speed tests to see how this affects it.
+ * - includes sub arrays
+ * - connections = md5 sum mapp to pear db object
+ * - results = [id] => map to pear db object
+ * - resultseq = sequence id for results & results field
+ * - resultfields = [id] => list of fields return from query (for use with toArray())
+ * - ini = mapping of database to ini file results
+ * - links = mapping of database to links file
+ * - lasterror = pear error objects for last error event.
+ * - config = aliased view of PEAR::getStaticPropery('DB_DataObject','options') * done for performance.
+ * - array of loaded classes by autoload method - to stop it doing file access request over and over again!
+ */
+$GLOBALS['_DB_DATAOBJECT']['RESULTS'] = array();
+$GLOBALS['_DB_DATAOBJECT']['RESULTSEQ'] = 1;
+$GLOBALS['_DB_DATAOBJECT']['RESULTFIELDS'] = array();
+$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'] = array();
+$GLOBALS['_DB_DATAOBJECT']['INI'] = array();
+$GLOBALS['_DB_DATAOBJECT']['LINKS'] = array();
+$GLOBALS['_DB_DATAOBJECT']['SEQUENCE'] = array();
+$GLOBALS['_DB_DATAOBJECT']['LASTERROR'] = null;
+$GLOBALS['_DB_DATAOBJECT']['CONFIG'] = array();
+$GLOBALS['_DB_DATAOBJECT']['CACHE'] = array();
+$GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = false;
+$GLOBALS['_DB_DATAOBJECT']['QUERYENDTIME'] = 0;
+
+
+
+// this will be horrifically slow!!!!
+// NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer (see define before..)
+// these two are BC/FC handlers for call in PHP4/5
+
+if ( substr(phpversion(),0,1) == 5) {
+ class DB_DataObject_Overload
+ {
+ function __call($method,$args)
+ {
+ $return = null;
+ $this->_call($method,$args,$return);
+ return $return;
+ }
+ function __sleep()
+ {
+ return array_keys(get_object_vars($this)) ;
+ }
+ }
+} else {
+ if (version_compare(phpversion(),'4.3.10','eq') && !defined('DB_DATAOBJECT_NO_OVERLOAD')) {
+ trigger_error(
+ "overload does not work with PHP4.3.10, either upgrade
+ (snaps.php.net) or more recent version
+ or define DB_DATAOBJECT_NO_OVERLOAD as per the manual.
+ ",E_USER_ERROR);
+ }
+
+ if (!function_exists('clone')) {
+ // emulate clone - as per php_compact, slow but really the correct behaviour..
+ eval('function clone($t) { $r = $t; if (method_exists($r,"__clone")) { $r->__clone(); } return $r; }');
+ }
+ eval('
+ class DB_DataObject_Overload {
+ function __call($method,$args,&$return) {
+ return $this->_call($method,$args,$return);
+ }
+ }
+ ');
+}
+
+
+
+
+
+
+ /*
+ *
+ * @package DB_DataObject
+ * @author Alan Knowles <alan@akbkhome.com>
+ * @since PHP 4.0
+ */
+
+class DB_DataObject extends DB_DataObject_Overload
+{
+ /**
+ * The Version - use this to check feature changes
+ *
+ * @access private
+ * @var string
+ */
+ var $_DB_DataObject_version = "1.8.8";
+
+ /**
+ * The Database table (used by table extends)
+ *
+ * @access private
+ * @var string
+ */
+ var $__table = ''; // database table
+
+ /**
+ * The Number of rows returned from a query
+ *
+ * @access public
+ * @var int
+ */
+ var $N = 0; // Number of rows returned from a query
+
+ /* ============================================================= */
+ /* Major Public Methods */
+ /* (designed to be optionally then called with parent::method()) */
+ /* ============================================================= */
+
+
+ /**
+ * Get a result using key, value.
+ *
+ * for example
+ * $object->get("ID",1234);
+ * Returns Number of rows located (usually 1) for success,
+ * and puts all the table columns into this classes variables
+ *
+ * see the fetch example on how to extend this.
+ *
+ * if no value is entered, it is assumed that $key is a value
+ * and get will then use the first key in keys()
+ * to obtain the key.
+ *
+ * @param string $k column
+ * @param string $v value
+ * @access public
+ * @return int No. of rows
+ */
+ function get($k = null, $v = null)
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+ $keys = array();
+
+ if ($v === null) {
+ $v = $k;
+ $keys = $this->keys();
+ if (!$keys) {
+ $this->raiseError("No Keys available for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ $k = $keys[0];
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("$k $v " .print_r($keys,true), "GET");
+ }
+
+ if ($v === null) {
+ $this->raiseError("No Value specified for get", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ $this->$k = $v;
+ return $this->find(1);
+ }
+
+ /**
+ * An autoloading, caching static get method using key, value (based on get)
+ *
+ * Usage:
+ * $object = DB_DataObject::staticGet("DbTable_mytable",12);
+ * or
+ * $object = DB_DataObject::staticGet("DbTable_mytable","name","fred");
+ *
+ * or write it into your extended class:
+ * function &staticGet($k,$v=NULL) { return DB_DataObject::staticGet("This_Class",$k,$v); }
+ *
+ * @param string $class class name
+ * @param string $k column (or value if using keys)
+ * @param string $v value (optional)
+ * @access public
+ * @return object
+ */
+ function &staticGet($class, $k, $v = null)
+ {
+ $lclass = strtolower($class);
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+
+
+
+ $key = "$k:$v";
+ if ($v === null) {
+ $key = $k;
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE");
+ }
+ if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) {
+ return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE");
+ }
+
+ $obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
+ if (PEAR::isError($obj)) {
+ DB_DataObject::raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
+ $r = false;
+ return $r;
+ }
+
+ if (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) {
+ $_DB_DATAOBJECT['CACHE'][$lclass] = array();
+ }
+ if (!$obj->get($k,$v)) {
+ DB_DataObject::raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
+
+ $r = false;
+ return $r;
+ }
+ $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
+ return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
+ }
+
+ /**
+ * find results, either normal or crosstable
+ *
+ * for example
+ *
+ * $object = new mytable();
+ * $object->ID = 1;
+ * $object->find();
+ *
+ *
+ * will set $object->N to number of rows, and expects next command to fetch rows
+ * will return $object->N
+ *
+ * @param boolean $n Fetch first result
+ * @access public
+ * @return mixed (number of rows returned, or true if numRows fetching is not supported)
+ */
+ function find($n = false)
+ {
+ global $_DB_DATAOBJECT;
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug($n, "find",1);
+ }
+ if (!$this->__table) {
+ // xdebug can backtrace this!
+ trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
+ }
+ $this->N = 0;
+ $query_before = $this->_query;
+ $this->_build_condition($this->table()) ;
+
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ /* We are checking for method modifyLimitQuery as it is PEAR DB specific */
+ $sql = 'SELECT ' .
+ $this->_query['data_select'] . " \n" .
+ ' FROM ' . ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table) . " \n" .
+ $this->_join . " \n" .
+ $this->_query['condition'] . " \n" .
+ $this->_query['group_by'] . " \n" .
+ $this->_query['having'] . " \n" .
+ $this->_query['order_by'] . " \n";
+
+ if ((!isset($_DB_DATAOBJECT['CONFIG']['db_driver'])) ||
+ ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
+ /* PEAR DB specific */
+
+ if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
+ $sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']);
+ }
+ } else {
+ /* theoretically MDB2! */
+ if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
+ $DB->setLimit($this->_query['limit_count'],$this->_query['limit_start']);
+ }
+ }
+
+
+ $this->_query($sql);
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("CHECK autofetchd $n", "find", 1);
+ }
+
+ // find(true)
+
+ $ret = $this->N;
+ if (!$ret && !empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
+ // clear up memory if nothing found!?
+ unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
+ }
+
+ if ($n && $this->N > 0 ) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("ABOUT TO AUTOFETCH", "find", 1);
+ }
+ $fs = $this->fetch();
+ // if fetch returns false (eg. failed), then the backend doesnt support numRows (eg. ret=true)
+ // - hence find() also returns false..
+ $ret = ($ret === true) ? $fs : $ret;
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("DONE", "find", 1);
+ }
+ $this->_query = $query_before;
+ return $ret;
+ }
+
+ /**
+ * fetches next row into this objects var's
+ *
+ * returns 1 on success 0 on failure
+ *
+ *
+ *
+ * Example
+ * $object = new mytable();
+ * $object->name = "fred";
+ * $object->find();
+ * $store = array();
+ * while ($object->fetch()) {
+ * echo $this->ID;
+ * $store[] = $object; // builds an array of object lines.
+ * }
+ *
+ * to add features to a fetch
+ * function fetch () {
+ * $ret = parent::fetch();
+ * $this->date_formated = date('dmY',$this->date);
+ * return $ret;
+ * }
+ *
+ * @access public
+ * @return boolean on success
+ */
+ function fetch()
+ {
+
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+ if (empty($this->N)) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("No data returned from FIND (eg. N is 0)","FETCH", 3);
+ }
+ return false;
+ }
+
+ if (empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]) ||
+ !is_object($result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]))
+ {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug('fetched on object after fetch completed (no results found)');
+ }
+ return false;
+ }
+
+
+ $array = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ASSOC);
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug(serialize($array),"FETCH");
+ }
+
+ // fetched after last row..
+ if ($array === null) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $t= explode(' ',microtime());
+
+ $this->debug("Last Data Fetch'ed after " .
+ ($t[0]+$t[1]- $_DB_DATAOBJECT['QUERYENDTIME'] ) .
+ " seconds",
+ "FETCH", 1);
+ }
+ // reduce the memory usage a bit... (but leave the id in, so count() works ok on it)
+ unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
+
+ // we need to keep a copy of resultfields locally so toArray() still works
+ // however we dont want to keep it in the global cache..
+
+ if (!empty($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
+ $this->_resultFields = $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid];
+ unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
+ }
+ // this is probably end of data!!
+ //DB_DataObject::raiseError("fetch: no data returned", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+ // make sure resultFields is always empty..
+ $this->_resultFields = false;
+
+ if (!isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
+ // note: we dont declare this to keep the print_r size down.
+ $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]= array_flip(array_keys($array));
+ }
+
+ foreach($array as $k=>$v) {
+ $kk = str_replace(".", "_", $k);
+ $kk = str_replace(" ", "_", $kk);
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
+ }
+ $this->$kk = $array[$k];
+ }
+
+ // set link flag
+ $this->_link_loaded=false;
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("{$this->__table} DONE", "fetchrow",2);
+ }
+ if (($this->_query !== false) && empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) {
+ $this->_query = false;
+ }
+ return true;
+ }
+
+ /**
+ * Adds a condition to the WHERE statement, defaults to AND
+ *
+ * $object->whereAdd(); //reset or cleaer ewhwer
+ * $object->whereAdd("ID > 20");
+ * $object->whereAdd("age > 20","OR");
+ *
+ * @param string $cond condition
+ * @param string $logic optional logic "OR" (defaults to "AND")
+ * @access public
+ * @return string|PEAR::Error - previous condition or Error when invalid args found
+ */
+ function whereAdd($cond = false, $logic = 'AND')
+ {
+ // for PHP5.2.3 - there is a bug with setting array properties of an object.
+ $_query = $this->_query;
+
+ if (!isset($this->_query) || ($_query === false)) {
+ return $this->raiseError(
+ "You cannot do two queries on the same object (clone it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+
+ if ($cond === false) {
+ $r = $this->_query['condition'];
+ $_query['condition'] = '';
+ $this->_query = $_query;
+ return preg_replace('/^\s+WHERE\s+/','',$r);
+ }
+ // check input...= 0 or ' ' == error!
+ if (!trim($cond)) {
+ return $this->raiseError("WhereAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+ $r = $_query['condition'];
+ if ($_query['condition']) {
+ $_query['condition'] .= " {$logic} ( {$cond} )";
+ $this->_query = $_query;
+ return $r;
+ }
+ $_query['condition'] = " WHERE ( {$cond} ) ";
+ $this->_query = $_query;
+ return $r;
+ }
+
+ /**
+ * Adds a order by condition
+ *
+ * $object->orderBy(); //clears order by
+ * $object->orderBy("ID");
+ * $object->orderBy("ID,age");
+ *
+ * @param string $order Order
+ * @access public
+ * @return none|PEAR::Error - invalid args only
+ */
+ function orderBy($order = false)
+ {
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ if ($order === false) {
+ $this->_query['order_by'] = '';
+ return;
+ }
+ // check input...= 0 or ' ' == error!
+ if (!trim($order)) {
+ return $this->raiseError("orderBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+
+ if (!$this->_query['order_by']) {
+ $this->_query['order_by'] = " ORDER BY {$order} ";
+ return;
+ }
+ $this->_query['order_by'] .= " , {$order}";
+ }
+
+ /**
+ * Adds a group by condition
+ *
+ * $object->groupBy(); //reset the grouping
+ * $object->groupBy("ID DESC");
+ * $object->groupBy("ID,age");
+ *
+ * @param string $group Grouping
+ * @access public
+ * @return none|PEAR::Error - invalid args only
+ */
+ function groupBy($group = false)
+ {
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ if ($group === false) {
+ $this->_query['group_by'] = '';
+ return;
+ }
+ // check input...= 0 or ' ' == error!
+ if (!trim($group)) {
+ return $this->raiseError("groupBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+
+
+ if (!$this->_query['group_by']) {
+ $this->_query['group_by'] = " GROUP BY {$group} ";
+ return;
+ }
+ $this->_query['group_by'] .= " , {$group}";
+ }
+
+ /**
+ * Adds a having clause
+ *
+ * $object->having(); //reset the grouping
+ * $object->having("sum(value) > 0 ");
+ *
+ * @param string $having condition
+ * @access public
+ * @return none|PEAR::Error - invalid args only
+ */
+ function having($having = false)
+ {
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ if ($having === false) {
+ $this->_query['having'] = '';
+ return;
+ }
+ // check input...= 0 or ' ' == error!
+ if (!trim($having)) {
+ return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+
+
+ if (!$this->_query['having']) {
+ $this->_query['having'] = " HAVING {$having} ";
+ return;
+ }
+ $this->_query['having'] .= " AND {$having}";
+ }
+
+ /**
+ * Sets the Limit
+ *
+ * $boject->limit(); // clear limit
+ * $object->limit(12);
+ * $object->limit(12,10);
+ *
+ * Note this will emit an error on databases other than mysql/postgress
+ * as there is no 'clean way' to implement it. - you should consider refering to
+ * your database manual to decide how you want to implement it.
+ *
+ * @param string $a limit start (or number), or blank to reset
+ * @param string $b number
+ * @access public
+ * @return none|PEAR::Error - invalid args only
+ */
+ function limit($a = null, $b = null)
+ {
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+
+ if ($a === null) {
+ $this->_query['limit_start'] = '';
+ $this->_query['limit_count'] = '';
+ return;
+ }
+ // check input...= 0 or ' ' == error!
+ if ((!is_int($a) && ((string)((int)$a) !== (string)$a))
+ || (($b !== null) && (!is_int($b) && ((string)((int)$b) !== (string)$b)))) {
+ return $this->raiseError("limit: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+ global $_DB_DATAOBJECT;
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ $this->_query['limit_start'] = ($b == null) ? 0 : (int)$a;
+ $this->_query['limit_count'] = ($b == null) ? (int)$a : (int)$b;
+
+ }
+
+ /**
+ * Adds a select columns
+ *
+ * $object->selectAdd(); // resets select to nothing!
+ * $object->selectAdd("*"); // default select
+ * $object->selectAdd("unixtime(DATE) as udate");
+ * $object->selectAdd("DATE");
+ *
+ * to prepend distict:
+ * $object->selectAdd('distinct ' . $object->selectAdd());
+ *
+ * @param string $k
+ * @access public
+ * @return mixed null or old string if you reset it.
+ */
+ function selectAdd($k = null)
+ {
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ if ($k === null) {
+ $old = $this->_query['data_select'];
+ $this->_query['data_select'] = '';
+ return $old;
+ }
+
+ // check input...= 0 or ' ' == error!
+ if (!trim($k)) {
+ return $this->raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+
+ if ($this->_query['data_select']) {
+ $this->_query['data_select'] .= ', ';
+ }
+ $this->_query['data_select'] .= " $k ";
+ }
+ /**
+ * Adds multiple Columns or objects to select with formating.
+ *
+ * $object->selectAs(null); // adds "table.colnameA as colnameA,table.colnameB as colnameB,......"
+ * // note with null it will also clear the '*' default select
+ * $object->selectAs(array('a','b'),'%s_x'); // adds "a as a_x, b as b_x"
+ * $object->selectAs(array('a','b'),'ddd_%s','ccc'); // adds "ccc.a as ddd_a, ccc.b as ddd_b"
+ * $object->selectAdd($object,'prefix_%s'); // calls $object->get_table and adds it all as
+ * objectTableName.colnameA as prefix_colnameA
+ *
+ * @param array|object|null the array or object to take column names from.
+ * @param string format in sprintf format (use %s for the colname)
+ * @param string table name eg. if you have joinAdd'd or send $from as an array.
+ * @access public
+ * @return void
+ */
+ function selectAs($from = null,$format = '%s',$tableName=false)
+ {
+ global $_DB_DATAOBJECT;
+
+ if ($this->_query === false) {
+ $this->raiseError(
+ "You cannot do two queries on the same object (copy it before finding)",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+
+ if ($from === null) {
+ // blank the '*'
+ $this->selectAdd();
+ $from = $this;
+ }
+
+
+ $table = $this->__table;
+ if (is_object($from)) {
+ $table = $from->__table;
+ $from = array_keys($from->table());
+ }
+
+ if ($tableName !== false) {
+ $table = $tableName;
+ }
+ $s = '%s';
+ if (!empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers'])) {
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $s = $DB->quoteIdentifier($s);
+ $format = $DB->quoteIdentifier($format);
+ }
+ foreach ($from as $k) {
+ $this->selectAdd(sprintf("{$s}.{$s} as {$format}",$table,$k,$k));
+ }
+ $this->_query['data_select'] .= "\n";
+ }
+ /**
+ * Insert the current objects variables into the database
+ *
+ * Returns the ID of the inserted element (if auto increment or sequences are used.)
+ *
+ * for example
+ *
+ * Designed to be extended
+ *
+ * $object = new mytable();
+ * $object->name = "fred";
+ * echo $object->insert();
+ *
+ * @access public
+ * @return mixed false on failure, int when auto increment or sequence used, otherwise true on success
+ */
+ function insert()
+ {
+ global $_DB_DATAOBJECT;
+
+ // we need to write to the connection (For nextid) - so us the real
+ // one not, a copyied on (as ret-by-ref fails with overload!)
+
+ if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ $this->_connect();
+ }
+
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ $items = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?
+ $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
+
+ if (!$items) {
+ $this->raiseError("insert:No table definition for {$this->__table}",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ $options = &$_DB_DATAOBJECT['CONFIG'];
+
+
+ $datasaved = 1;
+ $leftq = '';
+ $rightq = '';
+
+ $seqKeys = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]) ?
+ $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] :
+ $this->sequenceKey();
+
+ $key = isset($seqKeys[0]) ? $seqKeys[0] : false;
+ $useNative = isset($seqKeys[1]) ? $seqKeys[1] : false;
+ $seq = isset($seqKeys[2]) ? $seqKeys[2] : false;
+
+ $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["phptype"];
+
+
+ // nativeSequences or Sequences..
+
+ // big check for using sequences
+
+ if (($key !== false) && !$useNative) {
+
+ if (!$seq) {
+ $keyvalue = $DB->nextId($this->__table);
+ } else {
+ $f = $DB->getOption('seqname_format');
+ $DB->setOption('seqname_format','%s');
+ $keyvalue = $DB->nextId($seq);
+ $DB->setOption('seqname_format',$f);
+ }
+ if (PEAR::isError($keyvalue)) {
+ $this->raiseError($keyvalue->toString(), DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ $this->$key = $keyvalue;
+ }
+
+
+
+ foreach($items as $k => $v) {
+
+ // if we are using autoincrement - skip the column...
+ if ($key && ($k == $key) && $useNative) {
+ continue;
+ }
+
+
+ if (!isset($this->$k)) {
+ continue;
+ }
+ // dont insert data into mysql timestamps
+ // use query() if you really want to do this!!!!
+ if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
+ continue;
+ }
+
+ if ($leftq) {
+ $leftq .= ', ';
+ $rightq .= ', ';
+ }
+
+ $leftq .= ($quoteIdentifiers ? ($DB->quoteIdentifier($k) . ' ') : "$k ");
+
+ if (is_a($this->$k,'DB_DataObject_Cast')) {
+ $value = $this->$k->toString($v,$DB);
+ if (PEAR::isError($value)) {
+ $this->raiseError($value->toString() ,DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ $rightq .= $value;
+ continue;
+ }
+
+
+
+ if (!isset($options['disable_null_strings']) && is_string($this->$k) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
+ $rightq .= " NULL ";
+ continue;
+ }
+ // DATE is empty... on a col. that can be null..
+ // note: this may be usefull for time as well..
+ if (!$this->$k &&
+ (($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) &&
+ !($v & DB_DATAOBJECT_NOTNULL)) {
+
+ $rightq .= " NULL ";
+ continue;
+ }
+
+
+ if ($v & DB_DATAOBJECT_STR) {
+ $rightq .= $this->_quote((string) (
+ ($v & DB_DATAOBJECT_BOOL) ?
+ // this is thanks to the braindead idea of postgres to
+ // use t/f for boolean.
+ (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :
+ $this->$k
+ )) . " ";
+ continue;
+ }
+ if (is_numeric($this->$k)) {
+ $rightq .=" {$this->$k} ";
+ continue;
+ }
+ /* flag up string values - only at debug level... !!!??? */
+ if (is_object($this->$k) || is_array($this->$k)) {
+ $this->debug('ODD DATA: ' .$k . ' ' . print_r($this->$k,true),'ERROR');
+ }
+
+ // at present we only cast to integers
+ // - V2 may store additional data about float/int
+ $rightq .= ' ' . intval($this->$k) . ' ';
+
+ }
+
+ // not sure why we let empty insert here.. - I guess to generate a blank row..
+
+
+ if ($leftq || $useNative) {
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+
+ $r = $this->_query("INSERT INTO {$table} ($leftq) VALUES ($rightq) ");
+
+
+
+ if (PEAR::isError($r)) {
+ $this->raiseError($r);
+ return false;
+ }
+
+ if ($r < 1) {
+ return 0;
+ }
+
+
+ // now do we have an integer key!
+
+ if ($key && $useNative) {
+ switch ($dbtype) {
+ case 'mysql':
+ case 'mysqli':
+ $method = "{$dbtype}_insert_id";
+ $this->$key = $method(
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection
+ );
+ break;
+
+ case 'mssql':
+ // note this is not really thread safe - you should wrapp it with
+ // transactions = eg.
+ // $db->query('BEGIN');
+ // $db->insert();
+ // $db->query('COMMIT');
+ $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+ $method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
+ $mssql_key = $DB->$method("SELECT @@IDENTITY");
+ if (PEAR::isError($mssql_key)) {
+ $this->raiseError($mssql_key);
+ return false;
+ }
+ $this->$key = $mssql_key;
+ break;
+
+ case 'pgsql':
+ if (!$seq) {
+ $seq = $DB->getSequenceName(strtolower($this->__table));
+ }
+ $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+ $method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
+ $pgsql_key = $DB->$method("SELECT currval('".$seq . "')");
+
+
+ if (PEAR::isError($pgsql_key)) {
+ $this->raiseError($pgsql_key);
+ return false;
+ }
+ $this->$key = $pgsql_key;
+ break;
+
+ case 'ifx':
+ $this->$key = array_shift (
+ ifx_fetch_row (
+ ifx_query(
+ "select DBINFO('sqlca.sqlerrd1') FROM systables where tabid=1",
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection,
+ IFX_SCROLL
+ ),
+ "FIRST"
+ )
+ );
+ break;
+
+ }
+
+ }
+
+ if (isset($_DB_DATAOBJECT['CACHE'][strtolower(get_class($this))])) {
+ $this->_clear_cache();
+ }
+ if ($key) {
+ return $this->$key;
+ }
+ return true;
+ }
+ $this->raiseError("insert: No Data specifed for query", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+
+ /**
+ * Updates current objects variables into the database
+ * uses the keys() to decide how to update
+ * Returns the true on success
+ *
+ * for example
+ *
+ * $object = DB_DataObject::factory('mytable');
+ * $object->get("ID",234);
+ * $object->email="testing@test.com";
+ * if(!$object->update())
+ * echo "UPDATE FAILED";
+ *
+ * to only update changed items :
+ * $dataobject->get(132);
+ * $original = $dataobject; // clone/copy it..
+ * $dataobject->setFrom($_POST);
+ * if ($dataobject->validate()) {
+ * $dataobject->update($original);
+ * } // otherwise an error...
+ *
+ * performing global updates:
+ * $object = DB_DataObject::factory('mytable');
+ * $object->status = "dead";
+ * $object->whereAdd('age > 150');
+ * $object->update(DB_DATAOBJECT_WHEREADD_ONLY);
+ *
+ * @param object dataobject (optional) | DB_DATAOBJECT_WHEREADD_ONLY - used to only update changed items.
+ * @access public
+ * @return int rows affected or false on failure
+ */
+ function update($dataObject = false)
+ {
+ global $_DB_DATAOBJECT;
+ // connect will load the config!
+ $this->_connect();
+
+
+ $original_query = $this->_query;
+
+ $items = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?
+ $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
+
+ // only apply update against sequence key if it is set?????
+
+ $seq = $this->sequenceKey();
+ if ($seq[0] !== false) {
+ $keys = array($seq[0]);
+ if (empty($this->{$keys[0]}) && $dataObject !== true) {
+ $this->raiseError("update: trying to perform an update without
+ the key set, and argument to update is not
+ DB_DATAOBJECT_WHEREADD_ONLY
+ ", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ } else {
+ $keys = $this->keys();
+ }
+
+
+ if (!$items) {
+ $this->raiseError("update:No table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ $datasaved = 1;
+ $settings = '';
+ $this->_connect();
+
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $dbtype = $DB->dsn["phptype"];
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+ $options = $_DB_DATAOBJECT['CONFIG'];
+
+
+ foreach($items as $k => $v) {
+ if (!isset($this->$k)) {
+ continue;
+ }
+ // ignore stuff thats
+
+ // dont write things that havent changed..
+ if (($dataObject !== false) && isset($dataObject->$k) && ($dataObject->$k === $this->$k)) {
+ continue;
+ }
+
+ // - dont write keys to left.!!!
+ if (in_array($k,$keys)) {
+ continue;
+ }
+
+ // dont insert data into mysql timestamps
+ // use query() if you really want to do this!!!!
+ if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
+ continue;
+ }
+
+
+ if ($settings) {
+ $settings .= ', ';
+ }
+
+ $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
+
+ if (is_a($this->$k,'DB_DataObject_Cast')) {
+ $value = $this->$k->toString($v,$DB);
+ if (PEAR::isError($value)) {
+ $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
+ return false;
+ }
+ $settings .= "$kSql = $value ";
+ continue;
+ }
+
+ // special values ... at least null is handled...
+ if (!isset($options['disable_null_strings']) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
+ $settings .= "$kSql = NULL ";
+ continue;
+ }
+ // DATE is empty... on a col. that can be null..
+ // note: this may be usefull for time as well..
+ if (!$this->$k &&
+ (($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) &&
+ !($v & DB_DATAOBJECT_NOTNULL)) {
+
+ $settings .= "$kSql = NULL ";
+ continue;
+ }
+
+
+ if ($v & DB_DATAOBJECT_STR) {
+ $settings .= "$kSql = ". $this->_quote((string) (
+ ($v & DB_DATAOBJECT_BOOL) ?
+ // this is thanks to the braindead idea of postgres to
+ // use t/f for boolean.
+ (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :
+ $this->$k
+ )) . ' ';
+ continue;
+ }
+ if (is_numeric($this->$k)) {
+ $settings .= "$kSql = {$this->$k} ";
+ continue;
+ }
+ // at present we only cast to integers
+ // - V2 may store additional data about float/int
+ $settings .= "$kSql = " . intval($this->$k) . ' ';
+ }
+
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("got keys as ".serialize($keys),3);
+ }
+ if ($dataObject !== true) {
+ $this->_build_condition($items,$keys);
+ } else {
+ // prevent wiping out of data!
+ if (empty($this->_query['condition'])) {
+ $this->raiseError("update: global table update not available
+ do \$do->whereAdd('1=1'); if you really want to do that.
+ ", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ }
+
+
+
+ // echo " $settings, $this->condition ";
+ if ($settings && isset($this->_query) && $this->_query['condition']) {
+
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+
+ $r = $this->_query("UPDATE {$table} SET {$settings} {$this->_query['condition']} ");
+
+ // restore original query conditions.
+ $this->_query = $original_query;
+
+ if (PEAR::isError($r)) {
+ $this->raiseError($r);
+ return false;
+ }
+ if ($r < 1) {
+ return 0;
+ }
+
+ $this->_clear_cache();
+ return $r;
+ }
+ // restore original query conditions.
+ $this->_query = $original_query;
+
+ // if you manually specified a dataobject, and there where no changes - then it's ok..
+ if ($dataObject !== false) {
+ return true;
+ }
+
+ $this->raiseError(
+ "update: No Data specifed for query $settings , {$this->_query['condition']}",
+ DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+
+ /**
+ * Deletes items from table which match current objects variables
+ *
+ * Returns the true on success
+ *
+ * for example
+ *
+ * Designed to be extended
+ *
+ * $object = new mytable();
+ * $object->ID=123;
+ * echo $object->delete(); // builds a conditon
+ *
+ * $object = new mytable();
+ * $object->whereAdd('age > 12');
+ * $object->limit(1);
+ * $object->orderBy('age DESC');
+ * $object->delete(true); // dont use object vars, use the conditions, limit and order.
+ *
+ * @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
+ * we will build the condition only using the whereAdd's. Default is to
+ * build the condition only using the object parameters.
+ *
+ * @access public
+ * @return mixed True on success, false on failure, 0 on no data affected
+ */
+ function delete($useWhere = false)
+ {
+ global $_DB_DATAOBJECT;
+ // connect will load the config!
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+
+ $extra_cond = ' ' . (isset($this->_query['order_by']) ? $this->_query['order_by'] : '');
+
+ if (!$useWhere) {
+
+ $keys = $this->keys();
+ $this->_query = array(); // as it's probably unset!
+ $this->_query['condition'] = ''; // default behaviour not to use where condition
+ $this->_build_condition($this->table(),$keys);
+ // if primary keys are not set then use data from rest of object.
+ if (!$this->_query['condition']) {
+ $this->_build_condition($this->table(),array(),$keys);
+ }
+ $extra_cond = '';
+ }
+
+
+ // don't delete without a condition
+ if (($this->_query !== false) && $this->_query['condition']) {
+
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+ $sql = "DELETE FROM {$table} {$this->_query['condition']}{$extra_cond}";
+
+ // add limit..
+
+ if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
+
+ if (!isset($_DB_DATAOBJECT['CONFIG']['db_driver']) ||
+ ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
+ // pear DB
+ $sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']);
+
+ } else {
+ // MDB2
+ $DB->setLimit( $this->_query['limit_count'],$this->_query['limit_start']);
+ }
+
+ }
+
+
+ $r = $this->_query($sql);
+
+
+ if (PEAR::isError($r)) {
+ $this->raiseError($r);
+ return false;
+ }
+ if ($r < 1) {
+ return 0;
+ }
+ $this->_clear_cache();
+ return $r;
+ } else {
+ $this->raiseError("delete: No condition specifed for query", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+ }
+
+ /**
+ * fetches a specific row into this object variables
+ *
+ * Not recommended - better to use fetch()
+ *
+ * Returens true on success
+ *
+ * @param int $row row
+ * @access public
+ * @return boolean true on success
+ */
+ function fetchRow($row = null)
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ $this->_loadConfig();
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
+ }
+ if (!$this->__table) {
+ $this->raiseError("fetchrow: No table", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ if ($row === null) {
+ $this->raiseError("fetchrow: No row specified", DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ if (!$this->N) {
+ $this->raiseError("fetchrow: No results avaiable", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
+ }
+
+
+ $result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
+ $array = $result->fetchrow(DB_DATAOBJECT_FETCHMODE_ASSOC,$row);
+ if (!is_array($array)) {
+ $this->raiseError("fetchrow: No results available", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+
+ foreach($array as $k => $v) {
+ $kk = str_replace(".", "_", $k);
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
+ }
+ $this->$kk = $array[$k];
+ }
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("{$this->__table} DONE", "fetchrow", 3);
+ }
+ return true;
+ }
+
+ /**
+ * Find the number of results from a simple query
+ *
+ * for example
+ *
+ * $object = new mytable();
+ * $object->name = "fred";
+ * echo $object->count();
+ * echo $object->count(true); // dont use object vars.
+ * echo $object->count('distinct mycol'); count distinct mycol.
+ * echo $object->count('distinct mycol',true); // dont use object vars.
+ * echo $object->count('distinct'); // count distinct id (eg. the primary key)
+ *
+ *
+ * @param bool|string (optional)
+ * (true|false => see below not on whereAddonly)
+ * (string)
+ * "DISTINCT" => does a distinct count on the tables 'key' column
+ * otherwise => normally it counts primary keys - you can use
+ * this to do things like $do->count('distinct mycol');
+ *
+ * @param bool $whereAddOnly (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
+ * we will build the condition only using the whereAdd's. Default is to
+ * build the condition using the object parameters as well.
+ *
+ * @access public
+ * @return int
+ */
+ function count($countWhat = false,$whereAddOnly = false)
+ {
+ global $_DB_DATAOBJECT;
+
+ if (is_bool($countWhat)) {
+ $whereAddOnly = $countWhat;
+ }
+
+ $t = clone($this);
+ $items = $t->table();
+
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+
+
+ if (!isset($t->_query)) {
+ $this->raiseError(
+ "You cannot do run count after you have run fetch()",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+
+ if (!$whereAddOnly && $items) {
+ $t->_build_condition($items);
+ }
+ $keys = $this->keys();
+
+ if (!$keys[0] && !is_string($countWhat)) {
+ $this->raiseError(
+ "You cannot do run count without keys - use \$do->keys('id');",
+ DB_DATAOBJECT_ERROR_INVALIDARGS,PEAR_ERROR_DIE);
+ return false;
+
+ }
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+ $key_col = ($quoteIdentifiers ? $DB->quoteIdentifier($keys[0]) : $keys[0]);
+ $as = ($quoteIdentifiers ? $DB->quoteIdentifier('DATAOBJECT_NUM') : 'DATAOBJECT_NUM');
+
+ // support distinct on default keys.
+ $countWhat = (strtoupper($countWhat) == 'DISTINCT') ?
+ "DISTINCT {$table}.{$key_col}" : $countWhat;
+
+ $countWhat = is_string($countWhat) ? $countWhat : "{$table}.{$key_col}";
+
+ $r = $t->_query(
+ "SELECT count({$countWhat}) as $as
+ FROM $table {$t->_join} {$t->_query['condition']}");
+ if (PEAR::isError($r)) {
+ return false;
+ }
+
+ $result = &$_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
+ $l = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ORDERED);
+ // free the results - essential on oracle.
+ $t->free();
+
+ return (int) $l[0];
+ }
+
+ /**
+ * sends raw query to database
+ *
+ * Since _query has to be a private 'non overwriteable method', this is a relay
+ *
+ * @param string $string SQL Query
+ * @access public
+ * @return void or DB_Error
+ */
+ function query($string)
+ {
+ return $this->_query($string);
+ }
+
+
+ /**
+ * an escape wrapper around DB->escapeSimple()
+ * can be used when adding manual queries or clauses
+ * eg.
+ * $object->query("select * from xyz where abc like '". $object->escape($_GET['name']) . "'");
+ *
+ * @param string $string value to be escaped
+ * @param bool $likeEscape escapes % and _ as well. - so like queries can be protected.
+ * @access public
+ * @return string
+ */
+ function escape($string, $likeEscape=false)
+ {
+ global $_DB_DATAOBJECT;
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ // mdb2 uses escape...
+ $dd = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
+ $ret = ($dd == 'DB') ? $DB->escapeSimple($string) : $DB->escape($string);
+ if ($likeEscape) {
+ $ret = str_replace(array('_','%'), array('\_','\%'), $ret);
+ }
+ return $ret;
+
+ }
+
+ /* ==================================================== */
+ /* Major Private Vars */
+ /* ==================================================== */
+
+ /**
+ * The Database connection dsn (as described in the PEAR DB)
+ * only used really if you are writing a very simple application/test..
+ * try not to use this - it is better stored in configuration files..
+ *
+ * @access private
+ * @var string
+ */
+ var $_database_dsn = '';
+
+ /**
+ * The Database connection id (md5 sum of databasedsn)
+ *
+ * @access private
+ * @var string
+ */
+ var $_database_dsn_md5 = '';
+
+ /**
+ * The Database name
+ * created in __connection
+ *
+ * @access private
+ * @var string
+ */
+ var $_database = '';
+
+
+
+ /**
+ * The QUERY rules
+ * This replaces alot of the private variables
+ * used to build a query, it is unset after find() is run.
+ *
+ *
+ *
+ * @access private
+ * @var array
+ */
+ var $_query = array(
+ 'condition' => '', // the WHERE condition
+ 'group_by' => '', // the GROUP BY condition
+ 'order_by' => '', // the ORDER BY condition
+ 'having' => '', // the HAVING condition
+ 'limit_start' => '', // the LIMIT condition
+ 'limit_count' => '', // the LIMIT condition
+ 'data_select' => '*', // the columns to be SELECTed
+ );
+
+
+
+
+ /**
+ * Database result id (references global $_DB_DataObject[results]
+ *
+ * @access private
+ * @var integer
+ */
+ var $_DB_resultid;
+
+ /**
+ * ResultFields - on the last call to fetch(), resultfields is sent here,
+ * so we can clean up the memory.
+ *
+ * @access public
+ * @var array
+ */
+ var $_resultFields = false;
+
+
+ /* ============================================================== */
+ /* Table definition layer (started of very private but 'came out'*/
+ /* ============================================================== */
+
+ /**
+ * Autoload or manually load the table definitions
+ *
+ *
+ * usage :
+ * DB_DataObject::databaseStructure( 'databasename',
+ * parse_ini_file('mydb.ini',true),
+ * parse_ini_file('mydb.link.ini',true));
+ *
+ * obviously you dont have to use ini files.. (just return array similar to ini files..)
+ *
+ * It should append to the table structure array
+ *
+ *
+ * @param optional string name of database to assign / read
+ * @param optional array structure of database, and keys
+ * @param optional array table links
+ *
+ * @access public
+ * @return true or PEAR:error on wrong paramenters.. or false if no file exists..
+ * or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename)
+ */
+ function databaseStructure()
+ {
+
+ global $_DB_DATAOBJECT;
+
+ // Assignment code
+
+ if ($args = func_get_args()) {
+
+ if (count($args) == 1) {
+
+ // this returns all the tables and their structure..
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Loading Generator as databaseStructure called with args",1);
+ }
+
+ $x = new DB_DataObject;
+ $x->_database = $args[0];
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ $tables = $DB->getListOf('tables');
+ class_exists('DB_DataObject_Generator') ? '' :
+ require_once 'DB/DataObject/Generator.php';
+
+ foreach($tables as $table) {
+ $y = new DB_DataObject_Generator;
+ $y->fillTableSchema($x->_database,$table);
+ }
+ return $_DB_DATAOBJECT['INI'][$x->_database];
+ } else {
+
+ $_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
+ $_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
+
+ if (isset($args[1])) {
+ $_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
+ $_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
+ }
+ return true;
+ }
+
+ }
+
+
+
+ if (!$this->_database) {
+ $this->_connect();
+ }
+
+ // loaded already?
+ if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
+
+ // database loaded - but this is table is not available..
+ if (
+ empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])
+ && !empty($_DB_DATAOBJECT['CONFIG']['proxy'])
+ ) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Loading Generator to fetch Schema",1);
+ }
+ class_exists('DB_DataObject_Generator') ? '' :
+ require_once 'DB/DataObject/Generator.php';
+
+
+ $x = new DB_DataObject_Generator;
+ $x->fillTableSchema($this->_database,$this->__table);
+ }
+ return true;
+ }
+
+
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+
+ // if you supply this with arguments, then it will take those
+ // as the database and links array...
+
+ $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
+ array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
+ array() ;
+
+ if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
+ $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
+ $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
+ explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
+ }
+
+
+
+ foreach ($schemas as $ini) {
+ if (file_exists($ini) && is_file($ini)) {
+ $_DB_DATAOBJECT['INI'][$this->_database] = parse_ini_file($ini, true);
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ if (!is_readable ($ini)) {
+ $this->debug("ini file is not readable: $ini","databaseStructure",1);
+ } else {
+ $this->debug("Loaded ini file: $ini","databaseStructure",1);
+ }
+ }
+ } else {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Missing ini file: $ini","databaseStructure",1);
+ }
+ }
+
+ }
+ // now have we loaded the structure..
+
+ if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
+ return true;
+ }
+ // - if not try building it..
+ if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
+ class_exists('DB_DataObject_Generator') ? '' :
+ require_once 'DB/DataObject/Generator.php';
+
+ $x = new DB_DataObject_Generator;
+ $x->fillTableSchema($this->_database,$this->__table);
+ // should this fail!!!???
+ return true;
+ }
+ $this->debug("Cant find database schema: {$this->_database}/{$this->__table} \n".
+ "in links file data: " . print_r($_DB_DATAOBJECT['INI'],true),"databaseStructure",5);
+ // we have to die here!! - it causes chaos if we dont (including looping forever!)
+ $this->raiseError( "Unable to load schema for database and table (turn debugging up to 5 for full error message)", DB_DATAOBJECT_ERROR_INVALIDARGS, PEAR_ERROR_DIE);
+ return false;
+
+
+ }
+
+
+
+
+ /**
+ * Return or assign the name of the current table
+ *
+ *
+ * @param string optinal table name to set
+ * @access public
+ * @return string The name of the current table
+ */
+ function tableName()
+ {
+ $args = func_get_args();
+ if (count($args)) {
+ $this->__table = $args[0];
+ }
+ return $this->__table;
+ }
+
+ /**
+ * Return or assign the name of the current database
+ *
+ * @param string optional database name to set
+ * @access public
+ * @return string The name of the current database
+ */
+ function database()
+ {
+ $args = func_get_args();
+ if (count($args)) {
+ $this->_database = $args[0];
+ }
+ return $this->_database;
+ }
+
+ /**
+ * get/set an associative array of table columns
+ *
+ * @access public
+ * @param array key=>type array
+ * @return array (associative)
+ */
+ function table()
+ {
+
+ // for temporary storage of database fields..
+ // note this is not declared as we dont want to bloat the print_r output
+ $args = func_get_args();
+ if (count($args)) {
+ $this->_database_fields = $args[0];
+ }
+ if (isset($this->_database_fields)) {
+ return $this->_database_fields;
+ }
+
+
+ global $_DB_DATAOBJECT;
+ if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ $this->_connect();
+ }
+
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
+ return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
+ }
+
+ $this->databaseStructure();
+
+
+ $ret = array();
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
+ $ret = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
+ }
+
+ return $ret;
+ }
+
+ /**
+ * get/set an array of table primary keys
+ *
+ * set usage: $do->keys('id','code');
+ *
+ * This is defined in the table definition if it gets it wrong,
+ * or you do not want to use ini tables, you can override this.
+ * @param string optional set the key
+ * @param * optional set more keys
+ * @access private
+ * @return array
+ */
+ function keys()
+ {
+ // for temporary storage of database fields..
+ // note this is not declared as we dont want to bloat the print_r output
+ $args = func_get_args();
+ if (count($args)) {
+ $this->_database_keys = $args;
+ }
+ if (isset($this->_database_keys)) {
+ return $this->_database_keys;
+ }
+
+ global $_DB_DATAOBJECT;
+ if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ $this->_connect();
+ }
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
+ return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
+ }
+ $this->databaseStructure();
+
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
+ return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
+ }
+ return array();
+ }
+ /**
+ * get/set an sequence key
+ *
+ * by default it returns the first key from keys()
+ * set usage: $do->sequenceKey('id',true);
+ *
+ * override this to return array(false,false) if table has no real sequence key.
+ *
+ * @param string optional the key sequence/autoinc. key
+ * @param boolean optional use native increment. default false
+ * @param false|string optional native sequence name
+ * @access private
+ * @return array (column,use_native,sequence_name)
+ */
+ function sequenceKey()
+ {
+ global $_DB_DATAOBJECT;
+
+ // call setting
+ if (!$this->_database) {
+ $this->_connect();
+ }
+
+ if (!isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database])) {
+ $_DB_DATAOBJECT['SEQUENCE'][$this->_database] = array();
+ }
+
+
+ $args = func_get_args();
+ if (count($args)) {
+ $args[1] = isset($args[1]) ? $args[1] : false;
+ $args[2] = isset($args[2]) ? $args[2] : false;
+ $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = $args;
+ }
+ if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table])) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table];
+ }
+ // end call setting (eg. $do->sequenceKeys(a,b,c); )
+
+
+
+
+ $keys = $this->keys();
+ if (!$keys) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]
+ = array(false,false,false);
+ }
+
+
+ $table = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?
+ $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
+
+ $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
+
+ $usekey = $keys[0];
+
+
+
+ $seqname = false;
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
+ $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
+ if (strpos($usekey,':') !== false) {
+ list($usekey,$seqname) = explode(':',$usekey);
+ }
+ }
+
+
+ // if the key is not an integer - then it's not a sequence or native
+ if (empty($table[$usekey]) || !($table[$usekey] & DB_DATAOBJECT_INT)) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,false);
+ }
+
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'])) {
+ $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'];
+ if (is_string($ignore) && (strtoupper($ignore) == 'ALL')) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
+ }
+ if (is_string($ignore)) {
+ $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',',$ignore);
+ }
+ if (in_array($this->__table,$ignore)) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
+ }
+ }
+
+
+ $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
+
+ // if you are using an old ini file - go back to old behaviour...
+ if (is_numeric($realkeys[$usekey])) {
+ $realkeys[$usekey] = 'N';
+ }
+
+ // multiple unique primary keys without a native sequence...
+ if (($realkeys[$usekey] == 'K') && (count($keys) > 1)) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
+ }
+ // use native sequence keys...
+ // technically postgres native here...
+ // we need to get the new improved tabledata sorted out first.
+
+ if ( in_array($dbtype , array('psql', 'mysql', 'mysqli', 'mssql', 'ifx')) &&
+ ($table[$usekey] & DB_DATAOBJECT_INT) &&
+ isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
+ ) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,true,$seqname);
+ }
+ // if not a native autoinc, and we have not assumed all primary keys are sequence
+ if (($realkeys[$usekey] != 'N') &&
+ !empty($_DB_DATAOBJECT['CONFIG']['dont_use_pear_sequences'])) {
+ return array(false,false,false);
+ }
+ // I assume it's going to try and be a nextval DB sequence.. (not native)
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,false,$seqname);
+ }
+
+
+
+ /* =========================================================== */
+ /* Major Private Methods - the core part! */
+ /* =========================================================== */
+
+
+
+ /**
+ * clear the cache values for this class - normally done on insert/update etc.
+ *
+ * @access private
+ * @return void
+ */
+ function _clear_cache()
+ {
+ global $_DB_DATAOBJECT;
+
+ $class = strtolower(get_class($this));
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Clearing Cache for ".$class,1);
+ }
+
+ if (!empty($_DB_DATAOBJECT['CACHE'][$class])) {
+ unset($_DB_DATAOBJECT['CACHE'][$class]);
+ }
+ }
+
+
+ /**
+ * backend wrapper for quoting, as MDB2 and DB do it differently...
+ *
+ * @access private
+ * @return string quoted
+ */
+
+ function _quote($str)
+ {
+ global $_DB_DATAOBJECT;
+ return (empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ||
+ ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB'))
+ ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quoteSmart($str)
+ : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quote($str);
+ }
+
+
+ /**
+ * connects to the database
+ *
+ *
+ * TODO: tidy this up - This has grown to support a number of connection options like
+ * a) dynamic changing of ini file to change which database to connect to
+ * b) multi data via the table_{$table} = dsn ini option
+ * c) session based storage.
+ *
+ * @access private
+ * @return true | PEAR::error
+ */
+ function _connect()
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ $this->_loadConfig();
+ }
+ // Set database driver for reference
+ $db_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
+ // is it already connected ?
+
+ if ($this->_database_dsn_md5 && !empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ return $this->raiseError(
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->message,
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
+ );
+
+ }
+
+ if (!$this->_database) {
+ $this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
+ $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
+
+ $this->_database = ($db_driver != 'DB' && $hasGetDatabase)
+ ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase()
+ : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
+
+
+
+ if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite')
+ && is_file($this->_database)) {
+ $this->_database = basename($this->_database);
+ }
+ if ($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'ibase') {
+ $this->_database = substr(basename($this->_database), 0, -4);
+ }
+
+ }
+ // theoretically we have a md5, it's listed in connections and it's not an error.
+ // so everything is ok!
+ return true;
+
+ }
+
+ // it's not currently connected!
+ // try and work out what to use for the dsn !
+
+ $options= &$_DB_DATAOBJECT['CONFIG'];
+ $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
+
+ if (!$dsn) {
+ if (!$this->_database) {
+ $this->_database = isset($options["table_{$this->__table}"]) ? $options["table_{$this->__table}"] : null;
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Checking for database database_{$this->_database} in options","CONNECT");
+ }
+
+ if ($this->_database && !empty($options["database_{$this->_database}"])) {
+
+ $dsn = $options["database_{$this->_database}"];
+ } else if (!empty($options['database'])) {
+ $dsn = $options['database'];
+ }
+ }
+
+ // if still no database...
+ if (!$dsn) {
+ return $this->raiseError(
+ "No database name / dsn found anywhere",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG, PEAR_ERROR_DIE
+ );
+
+ }
+
+
+ if (is_string($dsn)) {
+ $this->_database_dsn_md5 = md5($dsn);
+ } else {
+ /// support array based dsn's
+ $this->_database_dsn_md5 = md5(serialize($dsn));
+ }
+
+ if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("USING CACHED CONNECTION", "CONNECT",3);
+ }
+ if (!$this->_database) {
+
+ $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
+ $this->_database = ($db_driver != 'DB' && $hasGetDatabase)
+ ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase()
+ : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
+
+ if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite')
+ && is_file($this->_database))
+ {
+ $this->_database = basename($this->_database);
+ }
+ }
+ return true;
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("NEW CONNECTION", "CONNECT",3);
+ /* actualy make a connection */
+ $this->debug(print_r($dsn,true) ." {$this->_database_dsn_md5}", "CONNECT",3);
+ }
+
+ // Note this is verbose deliberatly!
+
+ if ($db_driver == 'DB') {
+
+ /* PEAR DB connect */
+
+ // this allows the setings of compatibility on DB
+ $db_options = PEAR::getStaticProperty('DB','options');
+ require_once 'DB.php';
+ if ($db_options) {
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &DB::connect($dsn,$db_options);
+ } else {
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &DB::connect($dsn);
+ }
+
+ } else {
+ /* assumption is MDB2 */
+ require_once 'MDB2.php';
+ // this allows the setings of compatibility on MDB2
+ $db_options = PEAR::getStaticProperty('MDB2','options');
+ $db_options = is_array($db_options) ? $db_options : array();
+ $db_options['portability'] = isset($db_options['portability'] )
+ ? $db_options['portability'] : MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE;
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &MDB2::connect($dsn,$db_options);
+
+ }
+
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug(serialize($_DB_DATAOBJECT['CONNECTIONS']), "CONNECT",5);
+ }
+ if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ $this->debug($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->toString(), "CONNECT FAILED",5);
+ return $this->raiseError(
+ "Connect failed, turn on debugging to 5 see why",
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
+ );
+
+ }
+
+ if (!$this->_database) {
+ $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
+
+ $this->_database = ($db_driver != 'DB' && $hasGetDatabase)
+ ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase()
+ : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
+
+
+ if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite')
+ && is_file($this->_database))
+ {
+ $this->_database = basename($this->_database);
+ }
+ }
+
+ // Oracle need to optimize for portibility - not sure exactly what this does though :)
+ $c = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ return true;
+ }
+
+ /**
+ * sends query to database - this is the private one that must work
+ * - internal functions use this rather than $this->query()
+ *
+ * @param string $string
+ * @access private
+ * @return mixed none or PEAR_Error
+ */
+ function _query($string)
+ {
+ global $_DB_DATAOBJECT;
+ $this->_connect();
+
+
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ $options = &$_DB_DATAOBJECT['CONFIG'];
+
+ $_DB_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ?
+ 'DB': $_DB_DATAOBJECT['CONFIG']['db_driver'];
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug($string,$log="QUERY");
+
+ }
+
+ if (strtoupper($string) == 'BEGIN') {
+ if ($_DB_driver == 'DB') {
+ $DB->autoCommit(false);
+ } else {
+ $DB->beginTransaction();
+ }
+ // db backend adds begin anyway from now on..
+ return true;
+ }
+ if (strtoupper($string) == 'COMMIT') {
+ $res = $DB->commit();
+ if ($_DB_driver == 'DB') {
+ $DB->autoCommit(true);
+ }
+ return $res;
+ }
+
+ if (strtoupper($string) == 'ROLLBACK') {
+ $DB->rollback();
+ if ($_DB_driver == 'DB') {
+ $DB->autoCommit(true);
+ }
+ return true;
+ }
+
+
+ if (!empty($options['debug_ignore_updates']) &&
+ (strtolower(substr(trim($string), 0, 6)) != 'select') &&
+ (strtolower(substr(trim($string), 0, 4)) != 'show') &&
+ (strtolower(substr(trim($string), 0, 8)) != 'describe')) {
+
+ $this->debug('Disabling Update as you are in debug mode');
+ return $this->raiseError("Disabling Update as you are in debug mode", null) ;
+
+ }
+ //if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 1) {
+ // this will only work when PEAR:DB supports it.
+ //$this->debug($DB->getAll('explain ' .$string,DB_DATAOBJECT_FETCHMODE_ASSOC), $log="sql",2);
+ //}
+
+ // some sim
+ $t= explode(' ',microtime());
+ $_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0]+$t[1];
+
+
+ if ($_DB_driver == 'DB') {
+ $result = $DB->query($string);
+ } else {
+ switch (strtolower(substr(trim($string),0,6))) {
+
+ case 'insert':
+ case 'update':
+ case 'delete':
+ $result = $DB->exec($string);
+ break;
+
+ default:
+ $result = $DB->query($string);
+ break;
+ }
+ }
+
+
+
+ if (is_a($result,'PEAR_Error')) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug($result->toString(), "Query Error",1 );
+ }
+ return $this->raiseError($result);
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $t= explode(' ',microtime());
+ $_DB_DATAOBJECT['QUERYENDTIME'] = $t[0]+$t[1];
+ $this->debug('QUERY DONE IN '.($t[0]+$t[1]-$time)." seconds", 'query',1);
+ }
+ switch (strtolower(substr(trim($string),0,6))) {
+ case 'insert':
+ case 'update':
+ case 'delete':
+ if ($_DB_driver == 'DB') {
+ // pear DB specific
+ return $DB->affectedRows();
+ }
+ return $result;
+ }
+ if (is_object($result)) {
+ // lets hope that copying the result object is OK!
+
+ $_DB_resultid = $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ']++;
+ $_DB_DATAOBJECT['RESULTS'][$_DB_resultid] = $result;
+ $this->_DB_resultid = $_DB_resultid;
+ }
+ $this->N = 0;
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug(serialize($result), 'RESULT',5);
+ }
+ if (method_exists($result, 'numrows')) {
+ if ($_DB_driver == 'DB') {
+ $DB->expectError(DB_ERROR_UNSUPPORTED);
+ } else {
+ $DB->expectError(MDB2_ERROR_UNSUPPORTED);
+ }
+ $this->N = $result->numrows();
+ if (is_a($this->N,'PEAR_Error')) {
+ $this->N = true;
+ }
+ $DB->popExpect();
+ }
+ }
+
+ /**
+ * Builds the WHERE based on the values of of this object
+ *
+ * @param mixed $keys
+ * @param array $filter (used by update to only uses keys in this filter list).
+ * @param array $negative_filter (used by delete to prevent deleting using the keys mentioned..)
+ * @access private
+ * @return string
+ */
+ function _build_condition($keys, $filter = array(),$negative_filter=array())
+ {
+ global $_DB_DATAOBJECT;
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+ $options = $_DB_DATAOBJECT['CONFIG'];
+
+ // if we dont have query vars.. - reset them.
+ if ($this->_query === false) {
+ $x = new DB_DataObject;
+ $this->_query= $x->_query;
+ }
+
+ foreach($keys as $k => $v) {
+ // index keys is an indexed array
+ /* these filter checks are a bit suspicious..
+ - need to check that update really wants to work this way */
+
+ if ($filter) {
+ if (!in_array($k, $filter)) {
+ continue;
+ }
+ }
+ if ($negative_filter) {
+ if (in_array($k, $negative_filter)) {
+ continue;
+ }
+ }
+ if (!isset($this->$k)) {
+ continue;
+ }
+
+ $kSql = $quoteIdentifiers
+ ? ( $DB->quoteIdentifier($this->__table) . '.' . $DB->quoteIdentifier($k) )
+ : "{$this->__table}.{$k}";
+
+
+
+ if (is_a($this->$k,'DB_DataObject_Cast')) {
+ $dbtype = $DB->dsn["phptype"];
+ $value = $this->$k->toString($v,$DB);
+ if (PEAR::isError($value)) {
+ $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
+ return false;
+ }
+ if ((strtolower($value) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
+ $this->whereAdd(" $kSql IS NULL");
+ continue;
+ }
+ $this->whereAdd(" $kSql = $value");
+ continue;
+ }
+
+ if (!isset($options['disable_null_strings']) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
+ $this->whereAdd(" $kSql IS NULL");
+ continue;
+ }
+
+
+ if ($v & DB_DATAOBJECT_STR) {
+ $this->whereAdd(" $kSql = " . $this->_quote((string) (
+ ($v & DB_DATAOBJECT_BOOL) ?
+ // this is thanks to the braindead idea of postgres to
+ // use t/f for boolean.
+ (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :
+ $this->$k
+ )) );
+ continue;
+ }
+ if (is_numeric($this->$k)) {
+ $this->whereAdd(" $kSql = {$this->$k}");
+ continue;
+ }
+ /* this is probably an error condition! */
+ $this->whereAdd(" $kSql = ".intval($this->$k));
+ }
+ }
+
+ /**
+ * autoload Class relating to a table
+ * (depreciated - use ::factory)
+ *
+ * @param string $table table
+ * @access private
+ * @return string classname on Success
+ */
+ function staticAutoloadTable($table)
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+ $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
+ $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
+ $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
+
+ $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
+ $class = $ce ? $class : DB_DataObject::_autoloadClass($class);
+ return $class;
+ }
+
+
+ /**
+ * classic factory method for loading a table class
+ * usage: $do = DB_DataObject::factory('person')
+ * WARNING - this may emit a include error if the file does not exist..
+ * use @ to silence it (if you are sure it is acceptable)
+ * eg. $do = @DB_DataObject::factory('person')
+ *
+ * table name will eventually be databasename/table
+ * - and allow modular dataobjects to be written..
+ * (this also helps proxy creation)
+ *
+ *
+ * @param string $table tablename (use blank to create a new instance of the same class.)
+ * @access private
+ * @return DataObject|PEAR_Error
+ */
+
+
+
+ function factory($table = '') {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+
+ if ($table === '') {
+ if (is_a($this,'DB_DataObject') && strlen($this->__table)) {
+ $table = $this->__table;
+ } else {
+ return DB_DataObject::raiseError(
+ "factory did not recieve a table name",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ }
+ }
+
+
+ $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
+ $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
+ $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
+
+ $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
+ $class = $ce ? $class : DB_DataObject::_autoloadClass($class);
+
+ // proxy = full|light
+ if (!$class && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
+ $proxyMethod = 'getProxy'.$_DB_DATAOBJECT['CONFIG']['proxy'];
+ class_exists('DB_DataObject_Generator') ? '' :
+ require_once 'DB/DataObject/Generator.php';
+
+ $d = new DB_DataObject;
+
+ $d->__table = $table;
+ if (is_a($ret = $d->_connect(), 'PEAR_Error')) {
+ return $ret;
+ }
+
+ $x = new DB_DataObject_Generator;
+ return $x->$proxyMethod( $d->_database, $table);
+ }
+
+ if (!$class) {
+ return DB_DataObject::raiseError(
+ "factory could not find class $class from $table",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ }
+
+ return new $class;
+ }
+ /**
+ * autoload Class
+ *
+ * @param string $class Class
+ * @access private
+ * @return string classname on Success
+ */
+ function _autoloadClass($class)
+ {
+ global $_DB_DATAOBJECT;
+
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+ $class_prefix = empty($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
+ '' : $_DB_DATAOBJECT['CONFIG']['class_prefix'];
+
+ $table = substr($class,strlen($class_prefix));
+
+ // only include the file if it exists - and barf badly if it has parse errors :)
+ if (!empty($_DB_DATAOBJECT['CONFIG']['proxy']) || empty($_DB_DATAOBJECT['CONFIG']['class_location'])) {
+ return false;
+ }
+
+
+ if (strpos($_DB_DATAOBJECT['CONFIG']['class_location'],'%s') !== false) {
+ $file = sprintf($_DB_DATAOBJECT['CONFIG']['class_location'], preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)));
+ } else {
+ $file = $_DB_DATAOBJECT['CONFIG']['class_location'].'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
+ }
+
+ if (!file_exists($file)) {
+ $found = false;
+ foreach(explode(PATH_SEPARATOR, ini_get('include_path')) as $p) {
+ if (file_exists("$p/$file")) {
+ $file = "$p/$file";
+ $found = true;
+ break;
+ }
+ }
+ if (!$found) {
+ DB_DataObject::raiseError(
+ "autoload:Could not find class {$class} using class_location value",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ }
+
+ include_once $file;
+
+
+ $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
+
+ if (!$ce) {
+ DB_DataObject::raiseError(
+ "autoload:Could not autoload {$class}",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ return $class;
+ }
+
+
+
+ /**
+ * Have the links been loaded?
+ * if they have it contains a array of those variables.
+ *
+ * @access private
+ * @var boolean | array
+ */
+ var $_link_loaded = false;
+
+ /**
+ * Get the links associate array as defined by the links.ini file.
+ *
+ *
+ * Experimental... -
+ * Should look a bit like
+ * [local_col_name] => "related_tablename:related_col_name"
+ *
+ *
+ * @return array|null
+ * array = if there are links defined for this table.
+ * empty array - if there is a links.ini file, but no links on this table
+ * null - if no links.ini exists for this database (hence try auto_links).
+ * @access public
+ * @see DB_DataObject::getLinks(), DB_DataObject::getLink()
+ */
+
+ function links()
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ $this->_loadConfig();
+ }
+ // have to connect.. -> otherwise things break later.
+ $this->_connect();
+
+ if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
+ return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
+ }
+
+
+
+
+
+ // attempt to load links file here..
+
+ if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
+ $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
+ array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
+ array() ;
+
+ if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
+ $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
+ $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
+ explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
+ }
+
+
+
+ foreach ($schemas as $ini) {
+
+ $links =
+ isset($_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"]) ?
+ $_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"] :
+ str_replace('.ini','.links.ini',$ini);
+
+ if (empty($_DB_DATAOBJECT['LINKS'][$this->_database]) && file_exists($links) && is_file($links)) {
+ /* not sure why $links = ... here - TODO check if that works */
+ $_DB_DATAOBJECT['LINKS'][$this->_database] = parse_ini_file($links, true);
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Loaded links.ini file: $links","links",1);
+ }
+ } else {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Missing links.ini file: $links","links",1);
+ }
+ }
+ }
+ }
+
+
+ // if there is no link data at all on the file!
+ // we return null.
+ if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
+ return null;
+ }
+
+ if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
+ return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
+ }
+
+ return array();
+ }
+ /**
+ * load related objects
+ *
+ * There are two ways to use this, one is to set up a <dbname>.links.ini file
+ * into a static property named <dbname>.links and specifies the table joins,
+ * the other highly dependent on naming columns 'correctly' :)
+ * using colname = xxxxx_yyyyyy
+ * xxxxxx = related table; (yyyyy = user defined..)
+ * looks up table xxxxx, for value id=$this->xxxxx
+ * stores it in $this->_xxxxx_yyyyy
+ * you can change what object vars the links are stored in by
+ * changeing the format parameter
+ *
+ *
+ * @param string format (default _%s) where %s is the table name.
+ * @author Tim White <tim@cyface.com>
+ * @access public
+ * @return boolean , true on success
+ */
+ function getLinks($format = '_%s')
+ {
+
+ // get table will load the options.
+ if ($this->_link_loaded) {
+ return true;
+ }
+ $this->_link_loaded = false;
+ $cols = $this->table();
+ $links = $this->links();
+
+ $loaded = array();
+
+ if ($links) {
+ foreach($links as $key => $match) {
+ list($table,$link) = explode(':', $match);
+ $k = sprintf($format, str_replace('.', '_', $key));
+ // makes sure that '.' is the end of the key;
+ if ($p = strpos($key,'.')) {
+ $key = substr($key, 0, $p);
+ }
+
+ $this->$k = $this->getLink($key, $table, $link);
+
+ if (is_object($this->$k)) {
+ $loaded[] = $k;
+ }
+ }
+ $this->_link_loaded = $loaded;
+ return true;
+ }
+ // this is the autonaming stuff..
+ // it sends the column name down to getLink and lets that sort it out..
+ // if there is a links file then it is not used!
+ // IT IS DEPRECIATED!!!! - USE
+ if (!is_null($links)) {
+ return false;
+ }
+
+
+ foreach (array_keys($cols) as $key) {
+ if (!($p = strpos($key, '_'))) {
+ continue;
+ }
+ // does the table exist.
+ $k =sprintf($format, $key);
+ $this->$k = $this->getLink($key);
+ if (is_object($this->$k)) {
+ $loaded[] = $k;
+ }
+ }
+ $this->_link_loaded = $loaded;
+ return true;
+ }
+
+ /**
+ * return name from related object
+ *
+ * There are two ways to use this, one is to set up a <dbname>.links.ini file
+ * into a static property named <dbname>.links and specifies the table joins,
+ * the other is highly dependant on naming columns 'correctly' :)
+ *
+ * NOTE: the naming convention is depreciated!!! - use links.ini
+ *
+ * using colname = xxxxx_yyyyyy
+ * xxxxxx = related table; (yyyyy = user defined..)
+ * looks up table xxxxx, for value id=$this->xxxxx
+ * stores it in $this->_xxxxx_yyyyy
+ *
+ * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
+ *
+ *
+ * @param string $row either row or row.xxxxx
+ * @param string $table name of table to look up value in
+ * @param string $link name of column in other table to match
+ * @author Tim White <tim@cyface.com>
+ * @access public
+ * @return mixed object on success
+ */
+ function getLink($row, $table = null, $link = false)
+ {
+
+
+ // GUESS THE LINKED TABLE.. (if found - recursevly call self)
+
+ if ($table === null) {
+ $links = $this->links();
+
+ if (is_array($links)) {
+
+ if ($links[$row]) {
+ list($table,$link) = explode(':', $links[$row]);
+ if ($p = strpos($row,".")) {
+ $row = substr($row,0,$p);
+ }
+ return $this->getLink($row,$table,$link);
+
+ }
+
+ $this->raiseError(
+ "getLink: $row is not defined as a link (normally this is ok)",
+ DB_DATAOBJECT_ERROR_NODATA);
+
+ $r = false;
+ return $r;// technically a possible error condition?
+
+ }
+ // use the old _ method - this shouldnt happen if called via getLinks()
+ if (!($p = strpos($row, '_'))) {
+ $r = null;
+ return $r;
+ }
+ $table = substr($row, 0, $p);
+ return $this->getLink($row, $table);
+
+
+ }
+
+
+
+ if (!isset($this->$row)) {
+ $this->raiseError("getLink: row not set $row", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+
+ // check to see if we know anything about this table..
+
+ $obj = $this->factory($table);
+
+ if (!is_a($obj,'DB_DataObject')) {
+ $this->raiseError(
+ "getLink:Could not find class for row $row, table $table",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ if ($link) {
+ if ($obj->get($link, $this->$row)) {
+ $obj->free();
+ return $obj;
+ }
+ return false;
+ }
+
+ if ($obj->get($this->$row)) {
+ $obj->free();
+ return $obj;
+ }
+ return false;
+
+ }
+
+ /**
+ * IS THIS SUPPORTED/USED ANYMORE????
+ *return a list of options for a linked table
+ *
+ * This is highly dependant on naming columns 'correctly' :)
+ * using colname = xxxxx_yyyyyy
+ * xxxxxx = related table; (yyyyy = user defined..)
+ * looks up table xxxxx, for value id=$this->xxxxx
+ * stores it in $this->_xxxxx_yyyyy
+ *
+ * @access public
+ * @return array of results (empty array on failure)
+ */
+ function &getLinkArray($row, $table = null)
+ {
+
+ $ret = array();
+ if (!$table) {
+ $links = $this->links();
+
+ if (is_array($links)) {
+ if (!isset($links[$row])) {
+ // failed..
+ return $ret;
+ }
+ list($table,$link) = explode(':',$links[$row]);
+ } else {
+ if (!($p = strpos($row,'_'))) {
+ return $ret;
+ }
+ $table = substr($row,0,$p);
+ }
+ }
+
+ $c = $this->factory($table);
+
+ if (!is_a($c,'DB_DataObject')) {
+ $this->raiseError(
+ "getLinkArray:Could not find class for row $row, table $table",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG
+ );
+ return $ret;
+ }
+
+ // if the user defined method list exists - use it...
+ if (method_exists($c, 'listFind')) {
+ $c->listFind($this->id);
+ } else {
+ $c->find();
+ }
+ while ($c->fetch()) {
+ $ret[] = $c;
+ }
+ return $ret;
+ }
+
+ /**
+ * The JOIN condition
+ *
+ * @access private
+ * @var string
+ */
+ var $_join = '';
+
+ /**
+ * joinAdd - adds another dataobject to this, building a joined query.
+ *
+ * example (requires links.ini to be set up correctly)
+ * // get all the images for product 24
+ * $i = new DataObject_Image();
+ * $pi = new DataObjects_Product_image();
+ * $pi->product_id = 24; // set the product id to 24
+ * $i->joinAdd($pi); // add the product_image connectoin
+ * $i->find();
+ * while ($i->fetch()) {
+ * // do stuff
+ * }
+ * // an example with 2 joins
+ * // get all the images linked with products or productgroups
+ * $i = new DataObject_Image();
+ * $pi = new DataObject_Product_image();
+ * $pgi = new DataObject_Productgroup_image();
+ * $i->joinAdd($pi);
+ * $i->joinAdd($pgi);
+ * $i->find();
+ * while ($i->fetch()) {
+ * // do stuff
+ * }
+ *
+ *
+ * @param optional $obj object |array the joining object (no value resets the join)
+ * If you use an array here it should be in the format:
+ * array('local_column','remotetable:remote_column');
+ * if remotetable does not have a definition, you should
+ * use @ to hide the include error message..
+ *
+ *
+ * @param optional $joinType string | array
+ * 'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates
+ * just select ... from a,b,c with no join and
+ * links are added as where items.
+ *
+ * If second Argument is array, it is assumed to be an associative
+ * array with arguments matching below = eg.
+ * 'joinType' => 'INNER',
+ * 'joinAs' => '...'
+ * 'joinCol' => ....
+ * 'useWhereAsOn' => false,
+ *
+ * @param optional $joinAs string if you want to select the table as anther name
+ * useful when you want to select multiple columsn
+ * from a secondary table.
+
+ * @param optional $joinCol string The column on This objects table to match (needed
+ * if this table links to the child object in
+ * multiple places eg.
+ * user->friend (is a link to another user)
+ * user->mother (is a link to another user..)
+ *
+ * optional 'useWhereAsOn' bool default false;
+ * convert the where argments from the object being added
+ * into ON arguments.
+ *
+ *
+ * @return none
+ * @access public
+ * @author Stijn de Reede <sjr@gmx.co.uk>
+ */
+ function joinAdd($obj = false, $joinType='INNER', $joinAs=false, $joinCol=false)
+ {
+ global $_DB_DATAOBJECT;
+ if ($obj === false) {
+ $this->_join = '';
+ return;
+ }
+
+
+ $useWhereAsOn = false;
+ // support for 2nd argument as an array of options
+ if (is_array($joinType)) {
+ // new options can now go in here... (dont forget to document them)
+ $useWhereAsOn = !empty($joinType['useWhereAsOn']);
+ $joinCol = isset($joinType['joinCol']) ? $joinType['joinCol'] : $joinCol;
+ $joinAs = isset($joinType['joinAs']) ? $joinType['joinAs'] : $joinAs;
+ $joinType = isset($joinType['joinType']) ? $joinType['joinType'] : 'INNER';
+ }
+ // support for array as first argument
+ // this assumes that you dont have a links.ini for the specified table.
+ // and it doesnt exist as am extended dataobject!! - experimental.
+
+ $ofield = false; // object field
+ $tfield = false; // this field
+ $toTable = false;
+ if (is_array($obj)) {
+ $tfield = $obj[0];
+ list($toTable,$ofield) = explode(':',$obj[1]);
+ $obj = DB_DataObject::factory($toTable);
+
+ if (!$obj || is_a($obj,'PEAR_Error')) {
+ $obj = new DB_DataObject;
+ $obj->__table = $toTable;
+ }
+ $obj->_connect();
+ // set the table items to nothing.. - eg. do not try and match
+ // things in the child table...???
+ $items = array();
+ }
+
+ if (!is_object($obj) || !is_a($obj,'DB_DataObject')) {
+ return $this->raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA,PEAR_ERROR_DIE);
+ }
+ /* make sure $this->_database is set. */
+ $this->_connect();
+ $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+
+
+
+
+ /* look up the links for obj table */
+ //print_r($obj->links());
+ if (!$ofield && ($olinks = $obj->links())) {
+
+ foreach ($olinks as $k => $v) {
+ /* link contains {this column} = {linked table}:{linked column} */
+ $ar = explode(':', $v);
+
+ // Feature Request #4266 - Allow joins with multiple keys
+
+ $links_key_array = strpos($k,',');
+ if ($links_key_array !== false) {
+ $k = explode(',', $k);
+ }
+
+ $ar_array = strpos($ar[1],',');
+ if ($ar_array !== false) {
+ $ar[1] = explode(',', $ar[1]);
+ }
+
+ if ($ar[0] == $this->__table) {
+
+ // you have explictly specified the column
+ // and the col is listed here..
+ // not sure if 1:1 table could cause probs here..
+
+ if ($joinCol !== false) {
+ $this->raiseError(
+ "joinAdd: You cannot target a join column in the " .
+ "'link from' table ({$obj->__table}). " .
+ "Either remove the fourth argument to joinAdd() ".
+ "({$joinCol}), or alter your links.ini file.",
+ DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+
+ $ofield = $k;
+ $tfield = $ar[1];
+ break;
+ }
+ }
+ }
+
+ /* otherwise see if there are any links from this table to the obj. */
+ //print_r($this->links());
+ if (($ofield === false) && ($links = $this->links())) {
+ foreach ($links as $k => $v) {
+ /* link contains {this column} = {linked table}:{linked column} */
+ $ar = explode(':', $v);
+ // Feature Request #4266 - Allow joins with multiple keys
+ if (strpos($k, ',') !== false) {
+ $k = explode(',', $k);
+ }
+ if (strpos($ar[1], ',') !== false) {
+ $ar[1] = explode(',', $ar[1]);
+ }
+
+ if ($ar[0] == $obj->__table) {
+ if ($joinCol !== false) {
+ if ($k == $joinCol) {
+ $tfield = $k;
+ $ofield = $ar[1];
+ break;
+ } else {
+ continue;
+ }
+ } else {
+ $tfield = $k;
+ $ofield = $ar[1];
+ break;
+ }
+ }
+ }
+ }
+ // finally if these two table have column names that match do a join by default on them
+
+ if (($ofield === false) && $joinCol) {
+ $ofield = $joinCol;
+ $tfield = $joinCol;
+
+ }
+ /* did I find a conneciton between them? */
+
+ if ($ofield === false) {
+ $this->raiseError(
+ "joinAdd: {$obj->__table} has no link with {$this->__table}",
+ DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+ $joinType = strtoupper($joinType);
+
+ // we default to joining as the same name (this is remvoed later..)
+
+ if ($joinAs === false) {
+ $joinAs = $obj->__table;
+ }
+
+ $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+ $options = $_DB_DATAOBJECT['CONFIG'];
+
+ // not sure how portable adding database prefixes is..
+ $objTable = $quoteIdentifiers ?
+ $DB->quoteIdentifier($obj->__table) :
+ $obj->__table ;
+
+ $dbPrefix = '';
+ if (strlen($obj->_database) && in_array($DB->dsn['phptype'],array('mysql','mysqli'))) {
+ $dbPrefix = ($quoteIdentifiers
+ ? $DB->quoteIdentifier($obj->_database)
+ : $obj->_database) . '.';
+ }
+
+ // if they are the same, then dont add a prefix...
+ if ($obj->_database == $this->_database) {
+ $dbPrefix = '';
+ }
+ // as far as we know only mysql supports database prefixes..
+ // prefixing the database name is now the default behaviour,
+ // as it enables joining mutiple columns from multiple databases...
+
+ // prefix database (quoted if neccessary..)
+ $objTable = $dbPrefix . $objTable;
+
+ $cond = '';
+
+ // if obj only a dataobject - eg. no extended class has been defined..
+ // it obvioulsy cant work out what child elements might exist...
+ // until we get on the fly querying of tables..
+ // note: we have already checked that it is_a(db_dataobject earlier)
+ if ( strtolower(get_class($obj)) != 'db_dataobject') {
+
+ // now add where conditions for anything that is set in the object
+
+
+
+ $items = $obj->table();
+ // will return an array if no items..
+
+ // only fail if we where expecting it to work (eg. not joined on a array)
+
+ if (!$items) {
+ $this->raiseError(
+ "joinAdd: No table definition for {$obj->__table}",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+
+ foreach($items as $k => $v) {
+ if (!isset($obj->$k)) {
+ continue;
+ }
+
+ $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
+
+
+ if ($v & DB_DATAOBJECT_STR) {
+ $obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) (
+ ($v & DB_DATAOBJECT_BOOL) ?
+ // this is thanks to the braindead idea of postgres to
+ // use t/f for boolean.
+ (($obj->$k === 'f') ? 0 : (int)(bool) $obj->$k) :
+ $obj->$k
+ )));
+ continue;
+ }
+ if (is_numeric($obj->$k)) {
+ $obj->whereAdd("{$joinAs}.{$kSql} = {$obj->$k}");
+ continue;
+ }
+
+ if (is_a($obj->$k,'DB_DataObject_Cast')) {
+ $value = $obj->$k->toString($v,$DB);
+ if (PEAR::isError($value)) {
+ $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
+ return false;
+ }
+ if (!isset($options['disable_null_strings']) && strtolower($value) === 'null') {
+ $obj->whereAdd("{$joinAs}.{$kSql} IS NULL");
+ continue;
+ } else {
+ $obj->whereAdd("{$joinAs}.{$kSql} = $value");
+ continue;
+ }
+ }
+
+
+ /* this is probably an error condition! */
+ $obj->whereAdd("{$joinAs}.{$kSql} = 0");
+ }
+ if ($this->_query === false) {
+ $this->raiseError(
+ "joinAdd can not be run from a object that has had a query run on it,
+ clone the object or create a new one and use setFrom()",
+ DB_DATAOBJECT_ERROR_INVALIDARGS);
+ return false;
+ }
+ }
+
+ // and finally merge the whereAdd from the child..
+ if ($obj->_query['condition']) {
+ $cond = preg_replace('/^\sWHERE/i','',$obj->_query['condition']);
+
+ if (!$useWhereAsOn) {
+ $this->whereAdd($cond);
+ }
+ }
+
+
+
+
+ // nested (join of joined objects..)
+ $appendJoin = '';
+ if ($obj->_join) {
+ // postgres allows nested queries, with ()'s
+ // not sure what the results are with other databases..
+ // may be unpredictable..
+ if (in_array($DB->dsn["phptype"],array('pgsql'))) {
+ $objTable = "($objTable {$obj->_join})";
+ } else {
+ $appendJoin = $obj->_join;
+ }
+ }
+
+
+ // fix for #2216
+ // add the joinee object's conditions to the ON clause instead of the WHERE clause
+ if ($useWhereAsOn && strlen($cond)) {
+ $appendJoin = ' AND ' . $cond . ' ' . $appendJoin;
+ }
+
+
+
+ $table = $this->__table;
+
+ if ($quoteIdentifiers) {
+ $joinAs = $DB->quoteIdentifier($joinAs);
+ $table = $DB->quoteIdentifier($table);
+ $ofield = (is_array($ofield)) ? array_map(array($DB, 'quoteIdentifier'), $ofield) : $DB->quoteIdentifier($ofield);
+ $tfield = (is_array($tfield)) ? array_map(array($DB, 'quoteIdentifier'), $tfield) : $DB->quoteIdentifier($tfield);
+ }
+ // add database prefix if they are different databases
+
+
+ $fullJoinAs = '';
+ $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table) != $joinAs;
+ if ($addJoinAs) {
+ // join table a AS b - is only supported by a few databases and is probably not needed
+ // , however since it makes the whole Statement alot clearer we are leaving it in
+ // for those databases.
+ $fullJoinAs = in_array($DB->dsn["phptype"],array('mysql','mysqli','pgsql')) ? "AS {$joinAs}" : $joinAs;
+ } else {
+ // if
+ $joinAs = $dbPrefix . $joinAs;
+ }
+
+
+ switch ($joinType) {
+ case 'INNER':
+ case 'LEFT':
+ case 'RIGHT': // others??? .. cross, left outer, right outer, natural..?
+
+ // Feature Request #4266 - Allow joins with multiple keys
+ $this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
+ if (is_array($ofield)) {
+ $key_count = count($ofield);
+ for($i = 0; $i < $key_count; $i++) {
+ if ($i == 0) {
+ $this->_join .= " ON ({$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]}) ";
+ }
+ else {
+ $this->_join .= " AND {$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]} ";
+ }
+ }
+ $this->_join .= ' ' . $appendJoin . ' ';
+ } else {
+ $this->_join .= " ON ({$joinAs}.{$ofield}={$table}.{$tfield}) {$appendJoin} ";
+ }
+
+ break;
+
+ case '': // this is just a standard multitable select..
+ $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}";
+ $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}");
+ }
+
+
+ return true;
+
+ }
+
+ /**
+ * Copies items that are in the table definitions from an
+ * array or object into the current object
+ * will not override key values.
+ *
+ *
+ * @param array | object $from
+ * @param string $format eg. map xxxx_name to $object->name using 'xxxx_%s' (defaults to %s - eg. name -> $object->name
+ * @param boolean $skipEmpty (dont assign empty values if a column is empty (eg. '' / 0 etc...)
+ * @access public
+ * @return true on success or array of key=>setValue error message
+ */
+ function setFrom($from, $format = '%s', $skipEmpty=false)
+ {
+ global $_DB_DATAOBJECT;
+ $keys = $this->keys();
+ $items = $this->table();
+ if (!$items) {
+ $this->raiseError(
+ "setFrom:Could not find table definition for {$this->__table}",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return;
+ }
+ $overload_return = array();
+ foreach (array_keys($items) as $k) {
+ if (in_array($k,$keys)) {
+ continue; // dont overwrite keys
+ }
+ if (!$k) {
+ continue; // ignore empty keys!!! what
+ }
+ if (is_object($from) && isset($from->{sprintf($format,$k)})) {
+ $kk = (strtolower($k) == 'from') ? '_from' : $k;
+ if (method_exists($this,'set'.$kk)) {
+ $ret = $this->{'set'.$kk}($from->{sprintf($format,$k)});
+ if (is_string($ret)) {
+ $overload_return[$k] = $ret;
+ }
+ continue;
+ }
+ $this->$k = $from->{sprintf($format,$k)};
+ continue;
+ }
+
+ if (is_object($from)) {
+ continue;
+ }
+
+ if (empty($from[$k]) && $skipEmpty) {
+ continue;
+ }
+
+ if (!isset($from[sprintf($format,$k)])) {
+ continue;
+ }
+
+ $kk = (strtolower($k) == 'from') ? '_from' : $k;
+ if (method_exists($this,'set'. $kk)) {
+ $ret = $this->{'set'.$kk}($from[sprintf($format,$k)]);
+ if (is_string($ret)) {
+ $overload_return[$k] = $ret;
+ }
+ continue;
+ }
+ if (is_object($from[sprintf($format,$k)])) {
+ continue;
+ }
+ if (is_array($from[sprintf($format,$k)])) {
+ continue;
+ }
+ $ret = $this->fromValue($k,$from[sprintf($format,$k)]);
+ if ($ret !== true) {
+ $overload_return[$k] = 'Not A Valid Value';
+ }
+ //$this->$k = $from[sprintf($format,$k)];
+ }
+ if ($overload_return) {
+ return $overload_return;
+ }
+ return true;
+ }
+
+ /**
+ * Returns an associative array from the current data
+ * (kind of oblivates the idea behind DataObjects, but
+ * is usefull if you use it with things like QuickForms.
+ *
+ * you can use the format to return things like user[key]
+ * by sending it $object->toArray('user[%s]')
+ *
+ * will also return links converted to arrays.
+ *
+ * @param string sprintf format for array
+ * @param bool empty only return elemnts that have a value set.
+ *
+ * @access public
+ * @return array of key => value for row
+ */
+
+ function toArray($format = '%s', $hideEmpty = false)
+ {
+ global $_DB_DATAOBJECT;
+ $ret = array();
+ $rf = ($this->_resultFields !== false) ? $this->_resultFields :
+ (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ? $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
+ $ar = ($rf !== false) ?
+ array_merge($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid],$this->table()) :
+ $this->table();
+
+ foreach($ar as $k=>$v) {
+
+ if (!isset($this->$k)) {
+ if (!$hideEmpty) {
+ $ret[sprintf($format,$k)] = '';
+ }
+ continue;
+ }
+ // call the overloaded getXXXX() method. - except getLink and getLinks
+ if (method_exists($this,'get'.$k) && !in_array(strtolower($k),array('links','link'))) {
+ $ret[sprintf($format,$k)] = $this->{'get'.$k}();
+ continue;
+ }
+ // should this call toValue() ???
+ $ret[sprintf($format,$k)] = $this->$k;
+ }
+ if (!$this->_link_loaded) {
+ return $ret;
+ }
+ foreach($this->_link_loaded as $k) {
+ $ret[sprintf($format,$k)] = $this->$k->toArray();
+
+ }
+
+ return $ret;
+ }
+
+ /**
+ * validate the values of the object (usually prior to inserting/updating..)
+ *
+ * Note: This was always intended as a simple validation routine.
+ * It lacks understanding of field length, whether you are inserting or updating (and hence null key values)
+ *
+ * This should be moved to another class: DB_DataObject_Validate
+ * FEEL FREE TO SEND ME YOUR VERSION FOR CONSIDERATION!!!
+ *
+ * Usage:
+ * if (is_array($ret = $obj->validate())) { ... there are problems with the data ... }
+ *
+ * Logic:
+ * - defaults to only testing strings/numbers if numbers or strings are the correct type and null values are correct
+ * - validate Column methods : "validate{ROWNAME}()" are called if they are defined.
+ * These methods should return
+ * true = everything ok
+ * false|object = something is wrong!
+ *
+ * - This method loads and uses the PEAR Validate Class.
+ *
+ *
+ * @access public
+ * @return array of validation results (where key=>value, value=false|object if it failed) or true (if they all succeeded)
+ */
+ function validate()
+ {
+ global $_DB_DATAOBJECT;
+ require_once 'Validate.php';
+ $table = $this->table();
+ $ret = array();
+ $seq = $this->sequenceKey();
+ $options = $_DB_DATAOBJECT['CONFIG'];
+ foreach($table as $key => $val) {
+
+
+ // call user defined validation always...
+ $method = "Validate" . ucfirst($key);
+ if (method_exists($this, $method)) {
+ $ret[$key] = $this->$method();
+ continue;
+ }
+
+ // if not null - and it's not set.......
+
+ if (!isset($this->$key) && ($val & DB_DATAOBJECT_NOTNULL)) {
+ // dont check empty sequence key values..
+ if (($key == $seq[0]) && ($seq[1] == true)) {
+ continue;
+ }
+ $ret[$key] = false;
+ continue;
+ }
+
+
+ if (!isset($options['disable_null_strings']) && is_string($this->$key) && (strtolower($this->$key) == 'null')) {
+ if ($val & DB_DATAOBJECT_NOTNULL) {
+ $this->debug("'null' field used for '$key', but it is defined as NOT NULL", 'VALIDATION', 4);
+ $ret[$key] = false;
+ continue;
+ }
+ continue;
+ }
+
+ // ignore things that are not set. ?
+
+ if (!isset($this->$key)) {
+ continue;
+ }
+
+ // if the string is empty.. assume it is ok..
+ if (!is_object($this->$key) && !is_array($this->$key) && !strlen((string) $this->$key)) {
+ continue;
+ }
+
+ // dont try and validate cast objects - assume they are problably ok..
+ if (is_object($this->$key) && is_a($this->$key,'DB_DataObject_Cast')) {
+ continue;
+ }
+ // at this point if you have set something to an object, and it's not expected
+ // the Validate will probably break!!... - rightly so! (your design is broken,
+ // so issuing a runtime error like PEAR_Error is probably not appropriate..
+
+ switch (true) {
+ // todo: date time.....
+ case ($val & DB_DATAOBJECT_STR):
+ $ret[$key] = Validate::string($this->$key, VALIDATE_PUNCTUATION . VALIDATE_NAME);
+ continue;
+ case ($val & DB_DATAOBJECT_INT):
+ $ret[$key] = Validate::number($this->$key, array('decimal'=>'.'));
+ continue;
+ }
+ }
+ // if any of the results are false or an object (eg. PEAR_Error).. then return the array..
+ foreach ($ret as $key => $val) {
+ if ($val !== true) {
+ return $ret;
+ }
+ }
+ return true; // everything is OK.
+ }
+
+ /**
+ * Gets the DB object related to an object - so you can use funky peardb stuf with it :)
+ *
+ * @access public
+ * @return object The DB connection
+ */
+ function &getDatabaseConnection()
+ {
+ global $_DB_DATAOBJECT;
+
+ if (($e = $this->_connect()) !== true) {
+ return $e;
+ }
+ if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ $r = false;
+ return $r;
+ }
+ return $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ }
+
+
+ /**
+ * Gets the DB result object related to the objects active query
+ * - so you can use funky pear stuff with it - like pager for example.. :)
+ *
+ * @access public
+ * @return object The DB result object
+ */
+
+ function &getDatabaseResult()
+ {
+ global $_DB_DATAOBJECT;
+ $this->_connect();
+ if (!isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
+ $r = false;
+ return $r;
+ }
+ return $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
+ }
+
+ /**
+ * Overload Extension support
+ * - enables setCOLNAME/getCOLNAME
+ * if you define a set/get method for the item it will be called.
+ * otherwise it will just return/set the value.
+ * NOTE this currently means that a few Names are NO-NO's
+ * eg. links,link,linksarray, from, Databaseconnection,databaseresult
+ *
+ * note
+ * - set is automatically called by setFrom.
+ * - get is automatically called by toArray()
+ *
+ * setters return true on success. = strings on failure
+ * getters return the value!
+ *
+ * this fires off trigger_error - if any problems.. pear_error,
+ * has problems with 4.3.2RC2 here
+ *
+ * @access public
+ * @return true?
+ * @see overload
+ */
+
+
+ function _call($method,$params,&$return) {
+
+ //$this->debug("ATTEMPTING OVERLOAD? $method");
+ // ignore constructors : - mm
+ if (strtolower($method) == strtolower(get_class($this))) {
+ return true;
+ }
+ $type = strtolower(substr($method,0,3));
+ $class = get_class($this);
+ if (($type != 'set') && ($type != 'get')) {
+ return false;
+ }
+
+
+
+ // deal with naming conflick of setFrom = this is messy ATM!
+
+ if (strtolower($method) == 'set_from') {
+ $return = $this->toValue('from',isset($params[0]) ? $params[0] : null);
+ return true;
+ }
+
+ $element = substr($method,3);
+
+ // dont you just love php's case insensitivity!!!!
+
+ $array = array_keys(get_class_vars($class));
+ /* php5 version which segfaults on 5.0.3 */
+ if (class_exists('ReflectionClass')) {
+ $reflection = new ReflectionClass($class);
+ $array = array_keys($reflection->getdefaultProperties());
+ }
+
+ if (!in_array($element,$array)) {
+ // munge case
+ foreach($array as $k) {
+ $case[strtolower($k)] = $k;
+ }
+ if ((substr(phpversion(),0,1) == 5) && isset($case[strtolower($element)])) {
+ trigger_error("PHP5 set/get calls should match the case of the variable",E_USER_WARNING);
+ $element = strtolower($element);
+ }
+
+ // does it really exist?
+ if (!isset($case[$element])) {
+ return false;
+ }
+ // use the mundged case
+ $element = $case[$element]; // real case !
+ }
+
+
+ if ($type == 'get') {
+ $return = $this->toValue($element,isset($params[0]) ? $params[0] : null);
+ return true;
+ }
+
+
+ $return = $this->fromValue($element, $params[0]);
+
+ return true;
+
+
+ }
+
+
+ /**
+ * standard set* implementation.
+ *
+ * takes data and uses it to set dates/strings etc.
+ * normally called from __call..
+ *
+ * Current supports
+ * date = using (standard time format, or unixtimestamp).... so you could create a method :
+ * function setLastread($string) { $this->fromValue('lastread',strtotime($string)); }
+ *
+ * time = using strtotime
+ * datetime = using same as date - accepts iso standard or unixtimestamp.
+ * string = typecast only..
+ *
+ * TODO: add formater:: eg. d/m/Y for date! ???
+ *
+ * @param string column of database
+ * @param mixed value to assign
+ *
+ * @return true| false (False on error)
+ * @access public
+ * @see DB_DataObject::_call
+ */
+
+
+ function fromValue($col,$value)
+ {
+ global $_DB_DATAOBJECT;
+ $options = $_DB_DATAOBJECT['CONFIG'];
+ $cols = $this->table();
+ // dont know anything about this col..
+ if (!isset($cols[$col])) {
+ $this->$col = $value;
+ return true;
+ }
+ //echo "FROM VALUE $col, {$cols[$col]}, $value\n";
+ switch (true) {
+ // set to null and column is can be null...
+ case (!isset($options['disable_null_strings']) && (strtolower($value) == 'null') && (!($cols[$col] & DB_DATAOBJECT_NOTNULL))):
+ case (is_object($value) && is_a($value,'DB_DataObject_Cast')):
+ $this->$col = $value;
+ return true;
+
+ // fail on setting null on a not null field..
+ case (!isset($options['disable_null_strings']) && (strtolower($value) == 'null') && ($cols[$col] & DB_DATAOBJECT_NOTNULL)):
+ return false;
+
+ case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
+ // empty values get set to '' (which is inserted/updated as NULl
+ if (!$value) {
+ $this->$col = '';
+ }
+
+ if (is_numeric($value)) {
+ $this->$col = date('Y-m-d H:i:s', $value);
+ return true;
+ }
+
+ // eak... - no way to validate date time otherwise...
+ $this->$col = (string) $value;
+ return true;
+
+ case ($cols[$col] & DB_DATAOBJECT_DATE):
+ // empty values get set to '' (which is inserted/updated as NULl
+
+ if (!$value) {
+ $this->$col = '';
+ return true;
+ }
+
+ if (is_numeric($value)) {
+ $this->$col = date('Y-m-d',$value);
+ return true;
+ }
+
+ // try date!!!!
+ require_once 'Date.php';
+ $x = new Date($value);
+ $this->$col = $x->format("%Y-%m-%d");
+ return true;
+
+ case ($cols[$col] & DB_DATAOBJECT_TIME):
+ // empty values get set to '' (which is inserted/updated as NULl
+ if (!$value) {
+ $this->$col = '';
+ }
+
+ $guess = strtotime($value);
+ if ($guess != -1) {
+ $this->$col = date('H:i:s', $guess);
+ return $return = true;
+ }
+ // otherwise an error in type...
+ return false;
+
+ case ($cols[$col] & DB_DATAOBJECT_STR):
+
+ $this->$col = (string) $value;
+ return true;
+
+ // todo : floats numerics and ints...
+ default:
+ $this->$col = $value;
+ return true;
+ }
+
+
+
+ }
+ /**
+ * standard get* implementation.
+ *
+ * with formaters..
+ * supported formaters:
+ * date/time : %d/%m/%Y (eg. php strftime) or pear::Date
+ * numbers : %02d (eg. sprintf)
+ * NOTE you will get unexpected results with times like 0000-00-00 !!!
+ *
+ *
+ *
+ * @param string column of database
+ * @param format foramt
+ *
+ * @return true Description
+ * @access public
+ * @see DB_DataObject::_call(),strftime(),Date::format()
+ */
+ function toValue($col,$format = null)
+ {
+ if (is_null($format)) {
+ return $this->$col;
+ }
+ $cols = $this->table();
+ switch (true) {
+ case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
+ if (!$this->$col) {
+ return '';
+ }
+ $guess = strtotime($this->$col);
+ if ($guess != -1) {
+ return strftime($format, $guess);
+ }
+ // eak... - no way to validate date time otherwise...
+ return $this->$col;
+ case ($cols[$col] & DB_DATAOBJECT_DATE):
+ if (!$this->$col) {
+ return '';
+ }
+ $guess = strtotime($this->$col);
+ if ($guess != -1) {
+ return strftime($format,$guess);
+ }
+ // try date!!!!
+ require_once 'Date.php';
+ $x = new Date($this->$col);
+ return $x->format($format);
+
+ case ($cols[$col] & DB_DATAOBJECT_TIME):
+ if (!$this->$col) {
+ return '';
+ }
+ $guess = strtotime($this->$col);
+ if ($guess > -1) {
+ return strftime($format, $guess);
+ }
+ // otherwise an error in type...
+ return $this->$col;
+
+ case ($cols[$col] & DB_DATAOBJECT_MYSQLTIMESTAMP):
+ if (!$this->$col) {
+ return '';
+ }
+ require_once 'Date.php';
+
+ $x = new Date($this->$col);
+
+ return $x->format($format);
+
+
+ case ($cols[$col] & DB_DATAOBJECT_BOOL):
+
+ if ($cols[$col] & DB_DATAOBJECT_STR) {
+ // it's a 't'/'f' !
+ return ($this->$col === 't');
+ }
+ return (bool) $this->$col;
+
+
+ default:
+ return sprintf($format,$this->col);
+ }
+
+
+ }
+
+
+ /* ----------------------- Debugger ------------------ */
+
+ /**
+ * Debugger. - use this in your extended classes to output debugging information.
+ *
+ * Uses DB_DataObject::DebugLevel(x) to turn it on
+ *
+ * @param string $message - message to output
+ * @param string $logtype - bold at start
+ * @param string $level - output level
+ * @access public
+ * @return none
+ */
+ function debug($message, $logtype = 0, $level = 1)
+ {
+ global $_DB_DATAOBJECT;
+
+ if (empty($_DB_DATAOBJECT['CONFIG']['debug']) ||
+ (is_numeric($_DB_DATAOBJECT['CONFIG']['debug']) && $_DB_DATAOBJECT['CONFIG']['debug'] < $level)) {
+ return;
+ }
+ // this is a bit flaky due to php's wonderfull class passing around crap..
+ // but it's about as good as it gets..
+ $class = (isset($this) && is_a($this,'DB_DataObject')) ? get_class($this) : 'DB_DataObject';
+
+ if (!is_string($message)) {
+ $message = print_r($message,true);
+ }
+ if (!is_numeric( $_DB_DATAOBJECT['CONFIG']['debug']) && is_callable( $_DB_DATAOBJECT['CONFIG']['debug'])) {
+ return call_user_func($_DB_DATAOBJECT['CONFIG']['debug'], $class, $message, $logtype, $level);
+ }
+
+ if (!ini_get('html_errors')) {
+ echo "$class : $logtype : $message\n";
+ flush();
+ return;
+ }
+ if (!is_string($message)) {
+ $message = print_r($message,true);
+ }
+ $colorize = ($logtype == 'ERROR') ? '<font color="red">' : '<font>';
+ echo "<code>{$colorize}<B>$class: $logtype:</B> ". nl2br(htmlspecialchars($message)) . "</font></code><BR>\n";
+ }
+
+ /**
+ * sets and returns debug level
+ * eg. DB_DataObject::debugLevel(4);
+ *
+ * @param int $v level
+ * @access public
+ * @return none
+ */
+ function debugLevel($v = null)
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+ if ($v !== null) {
+ $r = isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
+ $_DB_DATAOBJECT['CONFIG']['debug'] = $v;
+ return $r;
+ }
+ return isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
+ }
+
+ /**
+ * Last Error that has occured
+ * - use $this->_lastError or
+ * $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+ *
+ * @access public
+ * @var object PEAR_Error (or false)
+ */
+ var $_lastError = false;
+
+ /**
+ * Default error handling is to create a pear error, but never return it.
+ * if you need to handle errors you should look at setting the PEAR_Error callback
+ * this is due to the fact it would wreck havoc on the internal methods!
+ *
+ * @param int $message message
+ * @param int $type type
+ * @param int $behaviour behaviour (die or continue!);
+ * @access public
+ * @return error object
+ */
+ function raiseError($message, $type = null, $behaviour = null)
+ {
+ global $_DB_DATAOBJECT;
+
+ if ($behaviour == PEAR_ERROR_DIE && !empty($_DB_DATAOBJECT['CONFIG']['dont_die'])) {
+ $behaviour = null;
+ }
+ $error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+
+ // this will never work totally with PHP's object model.
+ // as this is passed on static calls (like staticGet in our case)
+
+ if (isset($this) && is_object($this) && is_subclass_of($this,'db_dataobject')) {
+ $this->_lastError = $error;
+ }
+
+ $_DB_DATAOBJECT['LASTERROR'] = $error;
+
+ // no checks for production here?....... - we log errors before we throw them.
+ DB_DataObject::debug($message,'ERROR',1);
+
+
+ if (PEAR::isError($message)) {
+ $error = $message;
+ } else {
+ require_once 'DB/DataObject/Error.php';
+ $error = PEAR::raiseError($message, $type, $behaviour,
+ $opts=null, $userinfo=null, 'DB_DataObject_Error'
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Define the global $_DB_DATAOBJECT['CONFIG'] as an alias to PEAR::getStaticProperty('DB_DataObject','options');
+ *
+ * After Profiling DB_DataObject, I discoved that the debug calls where taking
+ * considerable time (well 0.1 ms), so this should stop those calls happening. as
+ * all calls to debug are wrapped with direct variable queries rather than actually calling the funciton
+ * THIS STILL NEEDS FURTHER INVESTIGATION
+ *
+ * @access public
+ * @return object an error object
+ */
+ function _loadConfig()
+ {
+ global $_DB_DATAOBJECT;
+
+ $_DB_DATAOBJECT['CONFIG'] = &PEAR::getStaticProperty('DB_DataObject','options');
+
+
+ }
+ /**
+ * Free global arrays associated with this object.
+ *
+ *
+ * @access public
+ * @return none
+ */
+ function free()
+ {
+ global $_DB_DATAOBJECT;
+
+ if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
+ unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
+ }
+ if (isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
+ unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
+ }
+ // clear the staticGet cache as well.
+ $this->_clear_cache();
+ // this is a huge bug in DB!
+ if (isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->num_rows = array();
+ }
+
+ }
+
+
+ /* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
+
+ function _get_table() { return $this->table(); }
+ function _get_keys() { return $this->keys(); }
+
+
+
+
+}
+// technially 4.3.2RC1 was broken!!
+// looks like 4.3.3 may have problems too....
+if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
+
+ if ((phpversion() != '4.3.2-RC1') && (version_compare( phpversion(), "4.3.1") > 0)) {
+ if (version_compare( phpversion(), "5") < 0) {
+ overload('DB_DataObject');
+ }
+ $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = true;
+ }
+}
+
diff --git a/_darcs/pristine/extlib/DB/DataObject/Cast.php b/_darcs/pristine/extlib/DB/DataObject/Cast.php
new file mode 100644
index 000000000..616abb55e
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/DataObject/Cast.php
@@ -0,0 +1,546 @@
+<?php
+/**
+ * Prototype Castable Object.. for DataObject queries
+ *
+ * Storage for Data that may be cast into a variety of formats.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB_DataObject
+ * @author Alan Knowles <alan@akbkhome.com>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: Cast.php,v 1.15 2005/07/07 05:30:53 alan_k Exp $
+ * @link http://pear.php.net/package/DB_DataObject
+ */
+
+/**
+*
+* Common usages:
+* // blobs
+* $data = DB_DataObject_Cast::blob($somefile);
+* $data = DB_DataObject_Cast::string($somefile);
+* $dataObject->someblobfield = $data
+*
+* // dates?
+* $d1 = new DB_DataObject_Cast::date('12/12/2000');
+* $d2 = new DB_DataObject_Cast::date(2000,12,30);
+* $d3 = new DB_DataObject_Cast::date($d1->year, $d1->month+30, $d1->day+30);
+*
+* // time, datetime.. ?????????
+*
+* // raw sql????
+* $data = DB_DataObject_Cast::sql('cast("123123",datetime)');
+* $data = DB_DataObject_Cast::sql('NULL');
+*
+* // int's/string etc. are proably pretty pointless..!!!!
+*
+*
+* inside DB_DataObject,
+* if (is_a($v,'db_dataobject_class')) {
+* $value .= $v->toString(DB_DATAOBJECT_INT,'mysql');
+* }
+*
+*
+*
+*
+
+*/
+class DB_DataObject_Cast {
+
+ /**
+ * Type of data Stored in the object..
+ *
+ * @var string (date|blob|.....?)
+ * @access public
+ */
+ var $type;
+
+ /**
+ * Data For date representation
+ *
+ * @var int day/month/year
+ * @access public
+ */
+ var $day;
+ var $month;
+ var $year;
+
+
+ /**
+ * Generic Data..
+ *
+ * @var string
+ * @access public
+ */
+
+ var $value;
+
+
+
+ /**
+ * Blob consructor
+ *
+ * create a Cast object from some raw data.. (binary)
+ *
+ *
+ * @param string (with binary data!)
+ *
+ * @return object DB_DataObject_Cast
+ * @access public
+ */
+
+ function blob($value) {
+ $r = new DB_DataObject_Cast;
+ $r->type = 'blob';
+ $r->value = $value;
+ return $r;
+ }
+
+
+ /**
+ * String consructor (actually use if for ints and everything else!!!
+ *
+ * create a Cast object from some string (not binary)
+ *
+ *
+ * @param string (with binary data!)
+ *
+ * @return object DB_DataObject_Cast
+ * @access public
+ */
+
+ function string($value) {
+ $r = new DB_DataObject_Cast;
+ $r->type = 'string';
+ $r->value = $value;
+ return $r;
+ }
+
+ /**
+ * SQL constructor (for raw SQL insert)
+ *
+ * create a Cast object from some sql
+ *
+ * @param string (with binary data!)
+ *
+ * @return object DB_DataObject_Cast
+ * @access public
+ */
+
+ function sql($value)
+ {
+ $r = new DB_DataObject_Cast;
+ $r->type = 'sql';
+ $r->value = $value;
+ return $r;
+ }
+
+
+ /**
+ * Date Constructor
+ *
+ * create a Cast object from some string (not binary)
+ * NO VALIDATION DONE, although some crappy re-calcing done!
+ *
+ * @param vargs... accepts
+ * dd/mm
+ * dd/mm/yyyy
+ * yyyy-mm
+ * yyyy-mm-dd
+ * array(yyyy,dd)
+ * array(yyyy,dd,mm)
+ *
+ *
+ *
+ * @return object DB_DataObject_Cast
+ * @access public
+ */
+
+ function date()
+ {
+ $args = func_get_args();
+ switch(count($args)) {
+ case 0: // no args = today!
+ $bits = explode('-',date('Y-m-d'));
+ break;
+ case 1: // one arg = a string
+
+ if (strpos($args[0],'/') !== false) {
+ $bits = array_reverse(explode('/',$args[0]));
+ } else {
+ $bits = explode('-',$args[0]);
+ }
+ break;
+ default: // 2 or more..
+ $bits = $args;
+ }
+ if (count($bits) == 1) { // if YYYY set day = 1st..
+ $bits[] = 1;
+ }
+
+ if (count($bits) == 2) { // if YYYY-DD set day = 1st..
+ $bits[] = 1;
+ }
+
+ // if year < 1970 we cant use system tools to check it...
+ // so we make a few best gueses....
+ // basically do date calculations for the year 2000!!!
+ // fix me if anyone has more time...
+ if (($bits[0] < 1975) || ($bits[0] > 2030)) {
+ $oldyear = $bits[0];
+ $bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],2000)));
+ $bits[0] = ($bits[0] - 2000) + $oldyear;
+ } else {
+ // now mktime
+ $bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],$bits[0])));
+ }
+ $r = new DB_DataObject_Cast;
+ $r->type = 'date';
+ list($r->year,$r->month,$r->day) = $bits;
+ return $r;
+ }
+
+
+
+ /**
+ * Data For time representation ** does not handle timezones!!
+ *
+ * @var int hour/minute/second
+ * @access public
+ */
+ var $hour;
+ var $minute;
+ var $second;
+
+
+ /**
+ * DateTime Constructor
+ *
+ * create a Cast object from a Date/Time
+ * Maybe should accept a Date object.!
+ * NO VALIDATION DONE, although some crappy re-calcing done!
+ *
+ * @param vargs... accepts
+ * noargs (now)
+ * yyyy-mm-dd HH:MM:SS (Iso)
+ * array(yyyy,mm,dd,HH,MM,SS)
+ *
+ *
+ * @return object DB_DataObject_Cast
+ * @access public
+ * @author therion 5 at hotmail
+ */
+
+ function dateTime()
+ {
+ $args = func_get_args();
+ switch(count($args)) {
+ case 0: // no args = now!
+ $datetime = date('Y-m-d G:i:s', mktime());
+
+ case 1:
+ // continue on from 0 args.
+ if (!isset($datetime)) {
+ $datetime = $args[0];
+ }
+
+ $parts = explode(' ', $datetime);
+ $bits = explode('-', $parts[0]);
+ $bits = array_merge($bits, explode(':', $parts[1]));
+ break;
+
+ default: // 2 or more..
+ $bits = $args;
+
+ }
+
+ if (count($bits) != 6) {
+ // PEAR ERROR?
+ return false;
+ }
+
+ $r = DB_DataObject_Cast::date($bits[0], $bits[1], $bits[2]);
+ if (!$r) {
+ return $r; // pass thru error (False) - doesnt happen at present!
+ }
+ // change the type!
+ $r->type = 'datetime';
+
+ // should we mathematically sort this out..
+ // (or just assume that no-one's dumb enough to enter 26:90:90 as a time!
+ $r->hour = $bits[3];
+ $r->minute = $bits[4];
+ $r->second = $bits[5];
+ return $r;
+
+ }
+
+
+
+ /**
+ * time Constructor
+ *
+ * create a Cast object from a Date/Time
+ * Maybe should accept a Date object.!
+ * NO VALIDATION DONE, and no-recalcing done!
+ *
+ * @param vargs... accepts
+ * noargs (now)
+ * HH:MM:SS (Iso)
+ * array(HH,MM,SS)
+ *
+ *
+ * @return object DB_DataObject_Cast
+ * @access public
+ * @author therion 5 at hotmail
+ */
+ function time()
+ {
+ $args = func_get_args();
+ switch (count($args)) {
+ case 0: // no args = now!
+ $time = date('G:i:s', mktime());
+
+ case 1:
+ // continue on from 0 args.
+ if (!isset($time)) {
+ $time = $args[0];
+ }
+ $bits = explode(':', $time);
+ break;
+
+ default: // 2 or more..
+ $bits = $args;
+
+ }
+
+ if (count($bits) != 3) {
+ return false;
+ }
+
+ // now take data from bits into object fields
+ $r = new DB_DataObject_Cast;
+ $r->type = 'time';
+ $r->hour = $bits[0];
+ $r->minute = $bits[1];
+ $r->second = $bits[2];
+ return $r;
+
+ }
+
+
+
+ /**
+ * get the string to use in the SQL statement for this...
+ *
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ */
+
+ function toString($to=false,$db)
+ {
+ // if $this->type is not set, we are in serious trouble!!!!
+ // values for to:
+ $method = 'toStringFrom'.$this->type;
+ return $this->$method($to,$db);
+ }
+
+ /**
+ * get the string to use in the SQL statement from a blob of binary data
+ * ** Suppots only blob->postgres::bytea
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ */
+ function toStringFromBlob($to,$db)
+ {
+ // first weed out invalid casts..
+ // in blobs can only be cast to blobs.!
+
+ // perhaps we should support TEXT fields???
+
+ if (!($to & DB_DATAOBJECT_BLOB)) {
+ return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::blob to something other than a blob!');
+ }
+
+ switch ($db->dsn["phptype"]) {
+ case 'pgsql':
+ return "'".pg_escape_bytea($this->value)."'::bytea";
+
+ case 'mysql':
+ return "'".mysql_real_escape_string($this->value,$db->connection)."'";
+
+ case 'mysqli':
+ // this is funny - the parameter order is reversed ;)
+ return "'".mysqli_real_escape_string($db->connection, $this->value)."'";
+
+
+
+ default:
+ return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
+ }
+
+ }
+
+ /**
+ * get the string to use in the SQL statement for a blob from a string!
+ * ** Suppots only string->postgres::bytea
+ *
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ */
+ function toStringFromString($to,$db)
+ {
+ // first weed out invalid casts..
+ // in blobs can only be cast to blobs.!
+
+ // perhaps we should support TEXT fields???
+ //
+
+ if (!($to & DB_DATAOBJECT_BLOB)) {
+ return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a blob!'.
+ ' (why not just use native features)');
+ }
+
+ switch ($db->dsn['phptype']) {
+ case 'pgsql':
+ return "'".pg_escape_string($this->value)."'::bytea";
+
+ case 'mysql':
+ return "'".mysql_real_escape_string($this->value,$db->connection)."'";
+
+
+ case 'mysqli':
+ return "'".mysqli_real_escape_string($db->connection, $this->value)."'";
+
+
+ default:
+ return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
+ }
+
+ }
+
+
+ /**
+ * get the string to use in the SQL statement for a date
+ *
+ *
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ */
+ function toStringFromDate($to,$db)
+ {
+ // first weed out invalid casts..
+ // in blobs can only be cast to blobs.!
+ // perhaps we should support TEXT fields???
+ //
+
+ if (($to !== false) && !($to & DB_DATAOBJECT_DATE)) {
+ return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a date!'.
+ ' (why not just use native features)');
+ }
+ return "'{$this->year}-{$this->month}-{$this->day}'";
+ }
+
+ /**
+ * get the string to use in the SQL statement for a datetime
+ *
+ *
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ * @author therion 5 at hotmail
+ */
+
+ function toStringFromDateTime($to,$db)
+ {
+ // first weed out invalid casts..
+ // in blobs can only be cast to blobs.!
+ // perhaps we should support TEXT fields???
+ if (($to !== false) &&
+ !($to & (DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME))) {
+ return PEAR::raiseError('Invalid Cast from a ' .
+ ' DB_DataObject_Cast::dateTime to something other than a datetime!' .
+ ' (try using native features)');
+ }
+ return "'{$this->year}-{$this->month}-{$this->day} {$this->hour}:{$this->minute}:{$this->second}'";
+ }
+
+ /**
+ * get the string to use in the SQL statement for a time
+ *
+ *
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ * @author therion 5 at hotmail
+ */
+
+ function toStringFromTime($to,$db)
+ {
+ // first weed out invalid casts..
+ // in blobs can only be cast to blobs.!
+ // perhaps we should support TEXT fields???
+ if (($to !== false) && !($to & DB_DATAOBJECT_TIME)) {
+ return PEAR::raiseError('Invalid Cast from a' .
+ ' DB_DataObject_Cast::time to something other than a time!'.
+ ' (try using native features)');
+ }
+ return "'{$this->hour}:{$this->minute}:{$this->second}'";
+ }
+
+ /**
+ * get the string to use in the SQL statement for a raw sql statement.
+ *
+ * @param int $to Type (DB_DATAOBJECT_*
+ * @param object $db DB Connection Object
+ *
+ *
+ * @return string
+ * @access public
+ */
+ function toStringFromSql($to,$db)
+ {
+ return $this->value;
+ }
+
+
+
+
+}
+
diff --git a/_darcs/pristine/extlib/DB/DataObject/Error.php b/_darcs/pristine/extlib/DB/DataObject/Error.php
new file mode 100644
index 000000000..05a741408
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/DataObject/Error.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * DataObjects error handler, loaded on demand...
+ *
+ * DB_DataObject_Error is a quick wrapper around pear error, so you can distinguish the
+ * error code source.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB_DataObject
+ * @author Alan Knowles <alan@akbkhome.com>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: Error.php,v 1.3 2005/03/23 02:35:35 alan_k Exp $
+ * @link http://pear.php.net/package/DB_DataObject
+ */
+
+
+class DB_DataObject_Error extends PEAR_Error
+{
+
+ /**
+ * DB_DataObject_Error constructor.
+ *
+ * @param mixed $code DB error code, or string with error message.
+ * @param integer $mode what "error mode" to operate in
+ * @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER
+ * @param mixed $debuginfo additional debug info, such as the last query
+ *
+ * @access public
+ *
+ * @see PEAR_Error
+ */
+ function DB_DataObject_Error($message = '', $code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
+ $level = E_USER_NOTICE)
+ {
+ $this->PEAR_Error('DB_DataObject Error: ' . $message, $code, $mode, $level);
+
+ }
+
+
+ // todo : - support code -> message handling, and translated error messages...
+
+
+
+}
diff --git a/_darcs/pristine/extlib/DB/DataObject/Generator.php b/_darcs/pristine/extlib/DB/DataObject/Generator.php
new file mode 100644
index 000000000..de16af692
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/DataObject/Generator.php
@@ -0,0 +1,1553 @@
+<?php
+/**
+ * Generation tools for DB_DataObject
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB_DataObject
+ * @author Alan Knowles <alan@akbkhome.com>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: Generator.php,v 1.141 2008/01/30 02:29:39 alan_k Exp $
+ * @link http://pear.php.net/package/DB_DataObject
+ */
+
+ /*
+ * Security Notes:
+ * This class uses eval to create classes on the fly.
+ * The table name and database name are used to check the database before writing the
+ * class definitions, we now check for quotes and semi-colon's in both variables
+ * so I cant see how it would be possible to generate code even if
+ * for some crazy reason you took the classname and table name from User Input.
+ *
+ * If you consider that wrong, or can prove it.. let me know!
+ */
+
+ /**
+ *
+ * Config _$ptions
+ * [DB_DataObject_Generator]
+ * ; optional default = DB/DataObject.php
+ * extends_location =
+ * ; optional default = DB_DataObject
+ * extends =
+ * ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
+ * generator_class_rewrite = ANY|specific_name // default is DB_DataObject
+ *
+ */
+
+/**
+ * Needed classes
+ * We lazy load here, due to problems with the tests not setting up include path correctly.
+ * FIXME!
+ */
+class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
+//require_once('Config.php');
+
+/**
+ * Generator class
+ *
+ * @package DB_DataObject
+ */
+class DB_DataObject_Generator extends DB_DataObject
+{
+ /* =========================================================== */
+ /* Utility functions - for building db config files */
+ /* =========================================================== */
+
+ /**
+ * Array of table names
+ *
+ * @var array
+ * @access private
+ */
+ var $tables;
+
+ /**
+ * associative array table -> array of table row objects
+ *
+ * @var array
+ * @access private
+ */
+ var $_definitions;
+
+ /**
+ * active table being output
+ *
+ * @var string
+ * @access private
+ */
+ var $table; // active tablename
+
+
+ /**
+ * The 'starter' = call this to start the process
+ *
+ * @access public
+ * @return none
+ */
+ function start()
+ {
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+
+ $databases = array();
+ foreach($options as $k=>$v) {
+ if (substr($k,0,9) == 'database_') {
+ $databases[substr($k,9)] = $v;
+ }
+ }
+
+ if (isset($options['database'])) {
+ if ($db_driver == 'DB') {
+ require_once 'DB.php';
+ $dsn = DB::parseDSN($options['database']);
+ } else {
+ require_once 'MDB2.php';
+ $dsn = MDB2::parseDSN($options['database']);
+ }
+
+ if (!isset($database[$dsn['database']])) {
+ $databases[$dsn['database']] = $options['database'];
+ }
+ }
+
+ foreach($databases as $databasename => $database) {
+ if (!$database) {
+ continue;
+ }
+ $this->debug("CREATING FOR $databasename\n");
+ $class = get_class($this);
+ $t = new $class;
+ $t->_database_dsn = $database;
+
+
+ $t->_database = $databasename;
+ if ($db_driver == 'DB') {
+ require_once 'DB.php';
+ $dsn = DB::parseDSN($database);
+ } else {
+ require_once 'MDB2.php';
+ $dsn = MDB2::parseDSN($database);
+ }
+
+ if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) {
+ $t->_database = basename($t->_database);
+ }
+ $t->_createTableList();
+
+ foreach(get_class_methods($class) as $method) {
+ if (substr($method,0,8 ) != 'generate') {
+ continue;
+ }
+ $this->debug("calling $method");
+ $t->$method();
+ }
+ }
+ $this->debug("DONE\n\n");
+ }
+
+ /**
+ * Output File was config object, now just string
+ * Used to generate the Tables
+ *
+ * @var string outputbuffer for table definitions
+ * @access private
+ */
+ var $_newConfig;
+
+ /**
+ * Build a list of tables;
+ * and store it in $this->tables and $this->_definitions[tablename];
+ *
+ * @access private
+ * @return none
+ */
+ function _createTableList()
+ {
+ $this->_connect();
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+
+ $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+
+ $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+ $is_MDB2 = ($db_driver != 'DB') ? true : false;
+
+ if (is_a($__DB , 'PEAR_Error')) {
+ return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
+ }
+
+ if (!$is_MDB2) {
+ // try getting a list of schema tables first. (postgres)
+ $__DB->expectError(DB_ERROR_UNSUPPORTED);
+ $this->tables = $__DB->getListOf('schema.tables');
+ $__DB->popExpect();
+ } else {
+ /**
+ * set portability and some modules to fetch the informations
+ */
+ $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
+ $__DB->loadModule('Manager');
+ $__DB->loadModule('Reverse');
+ }
+
+ if ((empty($this->tables) || is_a($this->tables , 'PEAR_Error'))) {
+ //if that fails fall back to clasic tables list.
+ if (!$is_MDB2) {
+ // try getting a list of schema tables first. (postgres)
+ $__DB->expectError(DB_ERROR_UNSUPPORTED);
+ $this->tables = $__DB->getListOf('tables');
+ $__DB->popExpect();
+ } else {
+ $this->tables = $__DB->manager->listTables();
+ $sequences = $__DB->manager->listSequences();
+ foreach ($sequences as $k => $v) {
+ $this->tables[] = $__DB->getSequenceName($v);
+ }
+ }
+ }
+
+ if (is_a($this->tables , 'PEAR_Error')) {
+ return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
+ }
+
+ // build views as well if asked to.
+ if (!empty($options['build_views'])) {
+ if (!$is_MDB2) {
+ $views = $__DB->getListOf('views');
+ } else {
+ $views = $__DB->manager->listViews();
+ }
+ if (is_a($views,'PEAR_Error')) {
+ return PEAR::raiseError(
+ 'Error getting Views (check the PEAR bug database for the fix to DB), ' .
+ $views->toString(),
+ null,
+ PEAR_ERROR_DIE
+ );
+ }
+ $this->tables = array_merge ($this->tables, $views);
+ }
+
+ // declare a temporary table to be filled with matching tables names
+ $tmp_table = array();
+
+
+ foreach($this->tables as $table) {
+ if (isset($options['generator_include_regex']) &&
+ !preg_match($options['generator_include_regex'],$table)) {
+ continue;
+ } else if (isset($options['generator_exclude_regex']) &&
+ preg_match($options['generator_exclude_regex'],$table)) {
+ continue;
+ }
+ // postgres strip the schema bit from the
+ if (!empty($options['generator_strip_schema'])) {
+ $bits = explode('.', $table,2);
+ $table = $bits[0];
+ if (count($bits) > 1) {
+ $table = $bits[1];
+ }
+ }
+ $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
+ $__DB->quoteIdentifier($table) : $table;
+
+ if (!$is_MDB2) {
+
+ $defs = $__DB->tableInfo($quotedTable);
+ } else {
+ $defs = $__DB->reverse->tableInfo($quotedTable);
+ // rename the length value, so it matches db's return.
+ foreach ($defs as $k => $v) {
+ if (!isset($defs[$k]['length'])) {
+ continue;
+ }
+ $defs[$k]['len'] = $defs[$k]['length'];
+ }
+ }
+
+ if (is_a($defs,'PEAR_Error')) {
+ // running in debug mode should pick this up as a big warning..
+ $this->raiseError('Error reading tableInfo, '. $defs->toString());
+ continue;
+ }
+ // cast all definitions to objects - as we deal with that better.
+
+
+
+ foreach($defs as $def) {
+ if (!is_array($def)) {
+ continue;
+ }
+
+ $this->_definitions[$table][] = (object) $def;
+
+ }
+ // we find a matching table, just store it into a temporary array
+ $tmp_table[] = $table;
+
+
+ }
+ // the temporary table array is now the right one (tables names matching
+ // with regex expressions have been removed)
+ $this->tables = $tmp_table;
+ //print_r($this->_definitions);
+ }
+
+ /**
+ * Auto generation of table data.
+ *
+ * it will output to db_oo_{database} the table definitions
+ *
+ * @access private
+ * @return none
+ */
+ function generateDefinitions()
+ {
+ $this->debug("Generating Definitions file: ");
+ if (!$this->tables) {
+ $this->debug("-- NO TABLES -- \n");
+ return;
+ }
+
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+
+
+ //$this->_newConfig = new Config('IniFile');
+ $this->_newConfig = '';
+ foreach($this->tables as $this->table) {
+ $this->_generateDefinitionsTable();
+ }
+ $this->_connect();
+ // dont generate a schema if location is not set
+ // it's created on the fly!
+ if (empty($options['schema_location']) && empty($options["ini_{$this->_database}"]) ) {
+ return;
+ }
+ if (!empty($options['generator_no_ini'])) { // built in ini files..
+ return;
+ }
+ $base = @$options['schema_location'];
+ if (isset($options["ini_{$this->_database}"])) {
+ $file = $options["ini_{$this->_database}"];
+ } else {
+ $file = "{$base}/{$this->_database}.ini";
+ }
+
+ if (!file_exists(dirname($file))) {
+ require_once 'System.php';
+ System::mkdir(array('-p','-m',0755,dirname($file)));
+ }
+ $this->debug("Writing ini as {$file}\n");
+ //touch($file);
+ $tmpname = tempnam(session_save_path(),'DataObject_');
+ //print_r($this->_newConfig);
+ $fh = fopen($tmpname,'w');
+ fwrite($fh,$this->_newConfig);
+ fclose($fh);
+ $perms = file_exists($file) ? fileperms($file) : 0755;
+ // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
+
+ if (!@rename($tmpname, $file)) {
+ unlink($file);
+ rename($tmpname, $file);
+ }
+ chmod($file,$perms);
+ //$ret = $this->_newConfig->writeInput($file,false);
+
+ //if (PEAR::isError($ret) ) {
+ // return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
+ // }
+ }
+
+ /**
+ * generate Foreign Keys (for links.ini)
+ * Currenly only works with mysql / mysqli
+ * to use, you must set option: generate_links=true
+ *
+ * @author Pascal Schöni
+ */
+ function generateForeignKeys()
+ {
+ $options = PEAR::getStaticProperty('DB_DataObject','options');
+ if (empty($options['generate_links'])) {
+ return false;
+ }
+ $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+ if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
+ echo "WARNING: cant handle non-mysql introspection for defaults.";
+ return; // cant handle non-mysql introspection for defaults.
+ }
+
+ $DB = $this->getDatabaseConnection();
+
+ $fk = array();
+
+ foreach($this->tables as $this->table) {
+ $res =& $DB->query('SHOW CREATE TABLE ' . $this->table);
+ if (PEAR::isError($res)) {
+ die($res->getMessage());
+ }
+
+ $text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
+ $treffer = array();
+ // Extract FOREIGN KEYS
+ preg_match_all(
+ "/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",
+ $text[1],
+ $treffer,
+ PREG_SET_ORDER);
+
+ if (count($treffer) < 1) {
+ continue;
+ }
+ for ($i = 0; $i < count($treffer); $i++) {
+ $fk[$this->table][$treffer[$i][1]] = $treffer[$i][2] . ":" . $treffer[$i][3];
+ }
+
+ }
+
+ $links_ini = "";
+
+ foreach($fk as $table => $details) {
+ $links_ini .= "[$table]\n";
+ foreach ($details as $col => $ref) {
+ $links_ini .= "$col = $ref\n";
+ }
+ $links_ini .= "\n";
+ }
+
+ // dont generate a schema if location is not set
+ // it's created on the fly!
+ $options = PEAR::getStaticProperty('DB_DataObject','options');
+
+ if (empty($options['schema_location'])) {
+ return;
+ }
+
+
+ $file = "{$options['schema_location']}/{$this->_database}.links.ini";
+
+ if (!file_exists(dirname($file))) {
+ require_once 'System.php';
+ System::mkdir(array('-p','-m',0755,dirname($file)));
+ }
+
+ $this->debug("Writing ini as {$file}\n");
+
+ //touch($file); // not sure why this is needed?
+ $tmpname = tempnam(session_save_path(),'DataObject_');
+
+ $fh = fopen($tmpname,'w');
+ fwrite($fh,$links_ini);
+ fclose($fh);
+ $perms = file_exists($file) ? fileperms($file) : 0755;
+ // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
+ if (!@rename($tmpname, $file)) {
+ unlink($file);
+ rename($tmpname, $file);
+ }
+ chmod($file, $perms);
+ }
+
+
+ /**
+ * The table geneation part
+ *
+ * @access private
+ * @return tabledef and keys array.
+ */
+ function _generateDefinitionsTable()
+ {
+ global $_DB_DATAOBJECT;
+
+ $defs = $this->_definitions[$this->table];
+ $this->_newConfig .= "\n[{$this->table}]\n";
+ $keys_out = "\n[{$this->table}__keys]\n";
+ $keys_out_primary = '';
+ $keys_out_secondary = '';
+ if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
+ echo "TABLE STRUCTURE FOR {$this->table}\n";
+ print_r($defs);
+ }
+ $DB = $this->getDatabaseConnection();
+ $dbtype = $DB->phptype;
+
+ $ret = array(
+ 'table' => array(),
+ 'keys' => array(),
+ );
+
+ $ret_keys_primary = array();
+ $ret_keys_secondary = array();
+
+
+
+ foreach($defs as $t) {
+
+ $n=0;
+ $write_ini = true;
+
+
+ switch (strtoupper($t->type)) {
+
+ case 'INT':
+ case 'INT2': // postgres
+ case 'INT4': // postgres
+ case 'INT8': // postgres
+ case 'SERIAL4': // postgres
+ case 'SERIAL8': // postgres
+ case 'INTEGER':
+ case 'TINYINT':
+ case 'SMALLINT':
+ case 'MEDIUMINT':
+ case 'BIGINT':
+ $type = DB_DATAOBJECT_INT;
+ if ($t->len == 1) {
+ $type += DB_DATAOBJECT_BOOL;
+ }
+ break;
+
+ case 'REAL':
+ case 'DOUBLE':
+ case 'DOUBLE PRECISION': // double precision (firebird)
+ case 'FLOAT':
+ case 'FLOAT4': // real (postgres)
+ case 'FLOAT8': // double precision (postgres)
+ case 'DECIMAL':
+ case 'MONEY': // mssql and maybe others
+ case 'NUMERIC':
+ case 'NUMBER': // oci8
+ $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
+ break;
+
+ case 'YEAR':
+ $type = DB_DATAOBJECT_INT;
+ break;
+
+ case 'BIT':
+ case 'BOOL':
+ case 'BOOLEAN':
+
+ $type = DB_DATAOBJECT_BOOL;
+ // postgres needs to quote '0'
+ if ($dbtype == 'pgsql') {
+ $type += DB_DATAOBJECT_STR;
+ }
+ break;
+
+ case 'STRING':
+ case 'CHAR':
+ case 'VARCHAR':
+ case 'VARCHAR2':
+ case 'TINYTEXT':
+
+ case 'ENUM':
+ case 'SET': // not really but oh well
+ case 'TIMESTAMPTZ': // postgres
+ case 'BPCHAR': // postgres
+ case 'INTERVAL': // postgres (eg. '12 days')
+
+ case 'CIDR': // postgres IP net spec
+ case 'INET': // postgres IP
+ case 'MACADDR': // postgress network Mac address.
+
+ case 'INTEGER[]': // postgres type
+ case 'BOOLEAN[]': // postgres type
+
+ $type = DB_DATAOBJECT_STR;
+ break;
+
+ case 'TEXT':
+ case 'MEDIUMTEXT':
+ case 'LONGTEXT':
+
+ $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
+ break;
+
+
+ case 'DATE':
+ $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
+ break;
+
+ case 'TIME':
+ $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
+ break;
+
+
+ case 'DATETIME':
+
+ $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
+ break;
+
+ case 'TIMESTAMP': // do other databases use this???
+
+ $type = ($dbtype == 'mysql') ?
+ DB_DATAOBJECT_MYSQLTIMESTAMP :
+ DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
+ break;
+
+
+ case 'TINYBLOB':
+ case 'BLOB': /// these should really be ignored!!!???
+ case 'MEDIUMBLOB':
+ case 'LONGBLOB':
+ case 'BYTEA': // postgres blob support..
+ $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
+ break;
+ default:
+ echo "*****************************************************************\n".
+ "** WARNING UNKNOWN TYPE **\n".
+ "** Found column '{$t->name}', of type '{$t->type}' **\n".
+ "** Please submit a bug, describe what type you expect this **\n".
+ "** column to be **\n".
+ "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
+ "** Try using MDB2 as the backend - eg set the config option **\n".
+ "** db_driver = MDB2 **\n".
+ "*****************************************************************\n";
+ $write_ini = false;
+ break;
+ }
+
+ if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
+ echo "*****************************************************************\n".
+ "** WARNING COLUMN NAME UNUSABLE **\n".
+ "** Found column '{$t->name}', of type '{$t->type}' **\n".
+ "** Since this column name can't be converted to a php variable **\n".
+ "** name, and the whole idea of mapping would result in a mess **\n".
+ "** This column has been ignored... **\n".
+ "*****************************************************************\n";
+ continue;
+ }
+
+ if (!strlen(trim($t->name))) {
+ continue; // is this a bug?
+ }
+
+ if (preg_match('/not[ _]null/i',$t->flags)) {
+ $type += DB_DATAOBJECT_NOTNULL;
+ }
+
+
+ if (in_array($t->name,array('null','yes','no','true','false'))) {
+ echo "*****************************************************************\n".
+ "** WARNING **\n".
+ "** Found column '{$t->name}', which is invalid in an .ini file **\n".
+ "** This line will not be writen to the file - you will have **\n".
+ "** define the keys()/method manually. **\n".
+ "*****************************************************************\n";
+ $write_ini = false;
+ } else {
+ $this->_newConfig .= "{$t->name} = $type\n";
+ }
+
+ $ret['table'][$t->name] = $type;
+ // i've no idea if this will work well on other databases?
+ // only use primary key or nextval(), cause the setFrom blocks you setting all key items...
+ // if no keys exist fall back to using unique
+ //echo "\n{$t->name} => {$t->flags}\n";
+ if (preg_match("/(auto_increment|nextval\()/i",rawurldecode($t->flags))
+ || (isset($t->autoincrement) && ($t->autoincrement === true))) {
+
+ // native sequences = 2
+ if ($write_ini) {
+ $keys_out_primary .= "{$t->name} = N\n";
+ }
+ $ret_keys_primary[$t->name] = 'N';
+
+ } else if (preg_match("/(primary|unique)/i",$t->flags)) {
+ // keys.. = 1
+ $key_type = 'K';
+ if (!preg_match("/(primary)/i",$t->flags)) {
+ $key_type = 'U';
+ }
+
+ if ($write_ini) {
+ $keys_out_secondary .= "{$t->name} = {$key_type}\n";
+ }
+ $ret_keys_secondary[$t->name] = $key_type;
+ }
+
+
+ }
+
+ $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
+ $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
+
+ if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
+ print_r(array("dump for {$this->table}", $ret));
+ }
+
+ return $ret;
+
+
+ }
+
+ /**
+ * Convert a table name into a class name -> override this if you want a different mapping
+ *
+ * @access public
+ * @return string class name;
+ */
+
+
+ function getClassNameFromTableName($table)
+ {
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
+ return $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table)));
+ }
+
+
+ /**
+ * Convert a table name into a file name -> override this if you want a different mapping
+ *
+ * @access public
+ * @return string file name;
+ */
+
+
+ function getFileNameFromTableName($table)
+ {
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $base = $options['class_location'];
+ if (strpos($base,'%s') !== false) {
+ $base = dirname($base);
+ }
+ if (!file_exists($base)) {
+ require_once 'System.php';
+ System::mkdir(array('-p',$base));
+ }
+ if (strpos($options['class_location'],'%s') !== false) {
+ $outfilename = sprintf($options['class_location'],
+ preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)));
+ } else {
+ $outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php";
+ }
+ return $outfilename;
+
+ }
+
+
+ /**
+ * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
+ *
+ * @access public
+ * @return string method name;
+ */
+
+
+ function getMethodNameFromColumnName($col)
+ {
+ return ucfirst($col);
+ }
+
+
+
+
+ /*
+ * building the class files
+ * for each of the tables output a file!
+ */
+ function generateClasses()
+ {
+ //echo "Generating Class files: \n";
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+
+
+ if ($extends = @$options['extends']) {
+ $this->_extends = $extends;
+ $this->_extendsFile = $options['extends_location'];
+ }
+
+ foreach($this->tables as $this->table) {
+ $this->table = trim($this->table);
+ $this->classname = $this->getClassNameFromTableName($this->table);
+ $i = '';
+ $outfilename = $this->getFileNameFromTableName($this->table);
+
+ $oldcontents = '';
+ if (file_exists($outfilename)) {
+ // file_get_contents???
+ $oldcontents = implode('',file($outfilename));
+ }
+
+ $out = $this->_generateClassTable($oldcontents);
+ $this->debug( "writing $this->classname\n");
+ $tmpname = tempnam(session_save_path(),'DataObject_');
+
+ $fh = fopen($tmpname, "w");
+ fputs($fh,$out);
+ fclose($fh);
+ $perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;
+
+ // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
+ if (!@rename($tmpname, $outfilename)) {
+ unlink($outfilename);
+ rename($tmpname, $outfilename);
+ }
+
+ chmod($outfilename, $perms);
+ }
+ //echo $out;
+ }
+
+ /**
+ * class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx
+ *
+ * @var string
+ * @access private
+ */
+ var $_extends = 'DB_DataObject';
+
+ /**
+ * line to use for require('DB/DataObject.php');
+ *
+ * @var string
+ * @access private
+ */
+ var $_extendsFile = "DB/DataObject.php";
+
+ /**
+ * class being generated
+ *
+ * @var string
+ * @access private
+ */
+ var $_className;
+
+ /**
+ * The table class geneation part - single file.
+ *
+ * @access private
+ * @return none
+ */
+ function _generateClassTable($input = '')
+ {
+ // title = expand me!
+ $foot = "";
+ $head = "<?php\n/**\n * Table Definition for {$this->table}\n";
+ $head .= $this->derivedHookPageLevelDocBlock();
+ $head .= " */\n";
+ $head .= $this->derivedHookExtendsDocBlock();
+
+
+ // requires
+ $head .= "require_once '{$this->_extendsFile}';\n\n";
+ // add dummy class header in...
+ // class
+ $head .= $this->derivedHookClassDocBlock();
+ $head .= "class {$this->classname} extends {$this->_extends} \n{";
+
+ $body = "\n ###START_AUTOCODE\n";
+ $body .= " /* the code below is auto generated do not remove the above tag */\n\n";
+ // table
+ $padding = (30 - strlen($this->table));
+ $padding = ($padding < 2) ? 2 : $padding;
+
+ $p = str_repeat(' ',$padding) ;
+
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+
+
+ $var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
+ $var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var;
+
+
+ $body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n";
+
+
+ // if we are using the option database_{databasename} = dsn
+ // then we should add var $_database = here
+ // as database names may not always match..
+
+
+
+
+ if (isset($options["database_{$this->_database}"])) {
+ $body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n";
+ }
+
+
+ if (!empty($options['generator_novars'])) {
+ $var = '//'.$var;
+ }
+
+ $defs = $this->_definitions[$this->table];
+
+ // show nice information!
+ $connections = array();
+ $sets = array();
+ foreach($defs as $t) {
+ if (!strlen(trim($t->name))) {
+ continue;
+ }
+ if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
+ echo "*****************************************************************\n".
+ "** WARNING COLUMN NAME UNUSABLE **\n".
+ "** Found column '{$t->name}', of type '{$t->type}' **\n".
+ "** Since this column name can't be converted to a php variable **\n".
+ "** name, and the whole idea of mapping would result in a mess **\n".
+ "** This column has been ignored... **\n".
+ "*****************************************************************\n";
+ continue;
+ }
+
+
+ $padding = (30 - strlen($t->name));
+ if ($padding < 2) $padding =2;
+ $p = str_repeat(' ',$padding) ;
+
+ $body .=" {$var} \${$t->name}; {$p}// {$t->type}({$t->len}) {$t->flags}\n";
+
+ // can not do set as PEAR::DB table info doesnt support it.
+ //if (substr($t->Type,0,3) == "set")
+ // $sets[$t->Field] = "array".substr($t->Type,3);
+ $body .= $this->derivedHookVar($t,$padding);
+ }
+
+ // THIS IS TOTALLY BORKED old FC creation
+ // IT WILL BE REMOVED!!!!! in DataObjects 1.6
+ // grep -r __clone * to find all it's uses
+ // and replace them with $x = clone($y);
+ // due to the change in the PHP5 clone design.
+
+ if ( substr(phpversion(),0,1) < 5) {
+ $body .= "\n";
+ $body .= " /* ZE2 compatibility trick*/\n";
+ $body .= " function __clone() { return \$this;}\n";
+ }
+
+ // simple creation tools ! (static stuff!)
+ $body .= "\n";
+ $body .= " /* Static get */\n";
+ $body .= " function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n";
+
+ // generate getter and setter methods
+ $body .= $this->_generateGetters($input);
+ $body .= $this->_generateSetters($input);
+
+ /*
+ theoretically there is scope here to introduce 'list' methods
+ based up 'xxxx_up' column!!! for heiracitcal trees..
+ */
+
+ // set methods
+ //foreach ($sets as $k=>$v) {
+ // $kk = strtoupper($k);
+ // $body .=" function getSets{$k}() { return {$v}; }\n";
+ //}
+
+ if (!empty($options['generator_no_ini'])) {
+ $def = $this->_generateDefinitionsTable(); // simplify this!?
+ $body .= $this->_generateTableFunction($def['table']);
+ $body .= $this->_generateKeysFunction($def['keys']);
+ $body .= $this->_generateSequenceKeyFunction($def);
+ $body .= $this->_generateDefaultsFunction($this->table, $def['table']);
+ } else if (!empty($options['generator_add_defaults'])) {
+ // I dont really like doing it this way (adding another option)
+ // but it helps on older projects.
+ $def = $this->_generateDefinitionsTable(); // simplify this!?
+ $body .= $this->_generateDefaultsFunction($this->table,$def['table']);
+
+ }
+ $body .= $this->derivedHookFunctions($input);
+
+ $body .= "\n /* the code above is auto generated do not remove the tag below */";
+ $body .= "\n ###END_AUTOCODE\n";
+
+
+ // stubs..
+
+ if (!empty($options['generator_add_validate_stubs'])) {
+ foreach($defs as $t) {
+ if (!strlen(trim($t->name))) {
+ continue;
+ }
+ $validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name);
+ // dont re-add it..
+ if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
+ continue;
+ }
+ $body .= "\n function {$validate_fname}()\n {\n return false;\n }\n";
+ }
+ }
+
+
+
+
+ $foot .= "}\n";
+ $full = $head . $body . $foot;
+
+ if (!$input) {
+ return $full;
+ }
+ if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input)) {
+ return $full;
+ }
+ if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
+ return $full;
+ }
+
+
+ /* this will only replace extends DB_DataObject by default,
+ unless use set generator_class_rewrite to ANY or a name*/
+
+ $class_rewrite = 'DB_DataObject';
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
+ $class_rewrite = 'DB_DataObject';
+ }
+ if ($class_rewrite == 'ANY') {
+ $class_rewrite = '[a-z_]+';
+ }
+
+ $input = preg_replace(
+ '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
+ "\nclass {$this->classname} extends {$this->_extends} \n{\n",
+ $input);
+
+ $ret = preg_replace(
+ '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
+ $body,$input);
+
+ if (!strlen($ret)) {
+ return PEAR::raiseError(
+ "PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n".
+ "pcre.backtrack_limit=1000000\n".
+ "pcre.recursion_limit=1000000\n"
+ ,null, PEAR_ERROR_DIE);
+ }
+
+ return $ret;
+ }
+
+ /**
+ * hook to add extra methods to all classes
+ *
+ * called once for each class, use with $this->table and
+ * $this->_definitions[$this->table], to get data out of the current table,
+ * use it to add extra methods to the default classes.
+ *
+ * @access public
+ * @return string added to class eg. functions.
+ */
+ function derivedHookFunctions($input = "")
+ {
+ // This is so derived generator classes can generate functions
+ // It MUST NOT be changed here!!!
+ return "";
+ }
+
+ /**
+ * hook for var lines
+ * called each time a var line is generated, override to add extra var
+ * lines
+ *
+ * @param object t containing type,len,flags etc. from tableInfo call
+ * @param int padding number of spaces
+ * @access public
+ * @return string added to class eg. functions.
+ */
+ function derivedHookVar(&$t,$padding)
+ {
+ // This is so derived generator classes can generate variabels
+ // It MUST NOT be changed here!!!
+ return "";
+ }
+
+ /**
+ * hook to add extra page-level (in terms of phpDocumentor) DocBlock
+ *
+ * called once for each class, use it add extra page-level docs
+ * @access public
+ * @return string added to class eg. functions.
+ */
+ function derivedHookPageLevelDocBlock() {
+ return '';
+ }
+
+ /**
+ * hook to add extra doc block (in terms of phpDocumentor) to extend string
+ *
+ * called once for each class, use it add extra comments to extends
+ * string (require_once...)
+ * @access public
+ * @return string added to class eg. functions.
+ */
+ function derivedHookExtendsDocBlock() {
+ return '';
+ }
+
+ /**
+ * hook to add extra class level DocBlock (in terms of phpDocumentor)
+ *
+ * called once for each class, use it add extra comments to class
+ * string (require_once...)
+ * @access public
+ * @return string added to class eg. functions.
+ */
+ function derivedHookClassDocBlock() {
+ return '';
+ }
+
+ /**
+
+ /**
+ * getProxyFull - create a class definition on the fly and instantate it..
+ *
+ * similar to generated files - but also evals the class definitoin code.
+ *
+ *
+ * @param string database name
+ * @param string table name of table to create proxy for.
+ *
+ *
+ * @return object Instance of class. or PEAR Error
+ * @access public
+ */
+ function getProxyFull($database,$table)
+ {
+
+ if ($err = $this->fillTableSchema($database,$table)) {
+ return $err;
+ }
+
+
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
+
+ if ($extends = @$options['extends']) {
+ $this->_extends = $extends;
+ $this->_extendsFile = $options['extends_location'];
+ }
+ $classname = $this->classname = $this->getClassNameFromTableName($this->table);
+
+ $out = $this->_generateClassTable();
+ //echo $out;
+ eval('?>'.$out);
+ return new $classname;
+
+ }
+
+ /**
+ * fillTableSchema - set the database schema on the fly
+ *
+ *
+ *
+ * @param string database name
+ * @param string table name of table to create schema info for
+ *
+ * @return none | PEAR::error()
+ * @access public
+ */
+ function fillTableSchema($database,$table)
+ {
+ global $_DB_DATAOBJECT;
+ // a little bit of sanity testing.
+ if ((false !== strpos($database,"'")) || (false !== strpos($database,";"))) {
+ return PEAR::raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE);
+ }
+
+ $this->_database = $database;
+
+ $this->_connect();
+ $table = trim($table);
+
+ // a little bit of sanity testing.
+ if ((false !== strpos($table,"'")) || (false !== strpos($table,";"))) {
+ return PEAR::raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
+ }
+ $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+
+
+ $options = PEAR::getStaticProperty('DB_DataObject','options');
+ $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+ $is_MDB2 = ($db_driver != 'DB') ? true : false;
+
+ if (!$is_MDB2) {
+ // try getting a list of schema tables first. (postgres)
+ $__DB->expectError(DB_ERROR_UNSUPPORTED);
+ $this->tables = $__DB->getListOf('schema.tables');
+ $__DB->popExpect();
+ } else {
+ /**
+ * set portability and some modules to fetch the informations
+ */
+ $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
+ $__DB->loadModule('Manager');
+ $__DB->loadModule('Reverse');
+ }
+ $quotedTable = !empty($options['quote_identifiers']) ?
+ $__DB->quoteIdentifier($table) : $table;
+
+ if (!$is_MDB2) {
+ $defs = $__DB->tableInfo($quotedTable);
+ } else {
+ $defs = $__DB->reverse->tableInfo($quotedTable);
+ foreach ($defs as $k => $v) {
+ if (!isset($defs[$k]['length'])) {
+ continue;
+ }
+ $defs[$k]['len'] = $defs[$k]['length'];
+ }
+ }
+
+
+
+
+ if (PEAR::isError($defs)) {
+ return $defs;
+ }
+ if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
+ $this->debug("getting def for $database/$table",'fillTable');
+ $this->debug(print_r($defs,true),'defs');
+ }
+ // cast all definitions to objects - as we deal with that better.
+
+
+ foreach($defs as $def) {
+ if (is_array($def)) {
+ $this->_definitions[$table][] = (object) $def;
+ }
+ }
+
+ $this->table = trim($table);
+ $ret = $this->_generateDefinitionsTable();
+
+ $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
+ $_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
+ return false;
+
+ }
+
+ /**
+ * Generate getter methods for class definition
+ *
+ * @param string $input Existing class contents
+ * @return string
+ * @access public
+ */
+ function _generateGetters($input)
+ {
+
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $getters = '';
+
+ // only generate if option is set to true
+ if (empty($options['generate_getters'])) {
+ return '';
+ }
+
+ // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
+ $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
+
+ $getters .= "\n\n";
+ $defs = $this->_definitions[$this->table];
+
+ // loop through properties and create getter methods
+ foreach ($defs = $defs as $t) {
+
+ // build mehtod name
+ $methodName = 'get' . $this->getMethodNameFromColumnName($t->name);
+
+ if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
+ continue;
+ }
+
+ $getters .= " /**\n";
+ $getters .= " * Getter for \${$t->name}\n";
+ $getters .= " *\n";
+ $getters .= (stristr($t->flags, 'multiple_key')) ? " * @return object\n"
+ : " * @return {$t->type}\n";
+ $getters .= " * @access public\n";
+ $getters .= " */\n";
+ $getters .= (substr(phpversion(),0,1) > 4) ? ' public '
+ : ' ';
+ $getters .= "function $methodName() {\n";
+ $getters .= " return \$this->{$t->name};\n";
+ $getters .= " }\n\n";
+ }
+
+
+ return $getters;
+ }
+
+
+ /**
+ * Generate setter methods for class definition
+ *
+ * @param string Existing class contents
+ * @return string
+ * @access public
+ */
+ function _generateSetters($input)
+ {
+
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $setters = '';
+
+ // only generate if option is set to true
+ if (empty($options['generate_setters'])) {
+ return '';
+ }
+
+ // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
+ $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
+
+ $setters .= "\n";
+ $defs = $this->_definitions[$this->table];
+
+ // loop through properties and create setter methods
+ foreach ($defs = $defs as $t) {
+
+ // build mehtod name
+ $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
+
+ if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
+ continue;
+ }
+
+ $setters .= " /**\n";
+ $setters .= " * Setter for \${$t->name}\n";
+ $setters .= " *\n";
+ $setters .= " * @param mixed input value\n";
+ $setters .= " * @access public\n";
+ $setters .= " */\n";
+ $setters .= (substr(phpversion(),0,1) > 4) ? ' public '
+ : ' ';
+ $setters .= "function $methodName(\$value) {\n";
+ $setters .= " \$this->{$t->name} = \$value;\n";
+ $setters .= " }\n\n";
+ }
+
+
+ return $setters;
+ }
+ /**
+ * Generate table Function - used when generator_no_ini is set.
+ *
+ * @param array table array.
+ * @return string
+ * @access public
+ */
+ function _generateTableFunction($def)
+ {
+ $defines = explode(',','INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP');
+
+ $ret = "\n" .
+ " function table()\n" .
+ " {\n" .
+ " return array(\n";
+
+ foreach($def as $k=>$v) {
+ $str = '0';
+ foreach($defines as $dn) {
+ if ($v & constant('DB_DATAOBJECT_' . $dn)) {
+ $str .= ' + DB_DATAOBJECT_' . $dn;
+ }
+ }
+ if (strlen($str) > 1) {
+ $str = substr($str,3); // strip the 0 +
+ }
+ // hopefully addslashes is good enough here!!!
+ $ret .= ' \''.addslashes($k).'\' => ' . $str . ",\n";
+ }
+ return $ret . " );\n" .
+ " }\n";
+
+
+
+ }
+ /**
+ * Generate keys Function - used generator_no_ini is set.
+ *
+ * @param array keys array.
+ * @return string
+ * @access public
+ */
+ function _generateKeysFunction($def)
+ {
+
+ $ret = "\n" .
+ " function keys()\n" .
+ " {\n" .
+ " return array(";
+
+ foreach($def as $k=>$type) {
+ // hopefully addslashes is good enough here!!!
+ $ret .= '\''.addslashes($k).'\', ';
+ }
+ $ret = preg_replace('#, $#', '', $ret);
+ return $ret . ");\n" .
+ " }\n";
+
+
+
+ }
+ /**
+ * Generate sequenceKey Function - used generator_no_ini is set.
+ *
+ * @param array table and key definition.
+ * @return string
+ * @access public
+ */
+ function _generateSequenceKeyFunction($def)
+ {
+
+ //print_r($def);
+ // DB_DataObject::debugLevel(5);
+ global $_DB_DATAOBJECT;
+ // print_r($def);
+
+
+ $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
+ $realkeys = $def['keys'];
+ $keys = array_keys($realkeys);
+ $usekey = isset($keys[0]) ? $keys[0] : false;
+ $table = $def['table'];
+
+
+ $seqname = false;
+
+
+
+
+ $ar = array(false,false,false);
+ if ($usekey !== false) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
+ $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
+ if (strpos($usekey,':') !== false) {
+ list($usekey,$seqname) = explode(':',$usekey);
+ }
+ }
+
+ if (in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) &&
+ ($table[$usekey] & DB_DATAOBJECT_INT) &&
+ isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
+ ) {
+ // use native sequence keys.
+ $ar = array($usekey,true,$seqname);
+ } else {
+ // use generated sequence keys..
+ if ($table[$usekey] & DB_DATAOBJECT_INT) {
+ $ar = array($usekey,false,$seqname);
+ }
+ }
+ }
+
+
+
+
+ $ret = "\n" .
+ " function sequenceKey() // keyname, use native, native name\n" .
+ " {\n" .
+ " return array(";
+ foreach($ar as $v) {
+ switch (gettype($v)) {
+ case 'boolean':
+ $ret .= ($v ? 'true' : 'false') . ', ';
+ break;
+
+ case 'string':
+ $ret .= "'" . $v . "', ";
+ break;
+
+ default: // eak
+ $ret .= "null, ";
+
+ }
+ }
+ $ret = preg_replace('#, $#', '', $ret);
+ return $ret . ");\n" .
+ " }\n";
+
+ }
+ /**
+ * Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
+ * Only supports mysql and mysqli ... welcome ideas for more..
+ *
+ *
+ * @param array table and key definition.
+ * @return string
+ * @access public
+ */
+ function _generateDefaultsFunction($table,$defs)
+ {
+ $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+ if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
+ return; // cant handle non-mysql introspection for defaults.
+ }
+ $options = PEAR::getStaticProperty('DB_DataObject','options');
+ $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+ $method = $db_driver == 'DB' ? 'getAll' : 'queryAll';
+ $res = $__DB->$method('DESCRIBE ' . $table,DB_FETCHMODE_ASSOC);
+ $defaults = array();
+ foreach($res as $ar) {
+ // this is initially very dumb... -> and it may mess up..
+ $type = $defs[$ar['Field']];
+
+ switch (true) {
+
+ case (is_null( $ar['Default'])):
+ $defaults[$ar['Field']] = 'null';
+ break;
+
+ case ($type & DB_DATAOBJECT_DATE):
+ case ($type & DB_DATAOBJECT_TIME):
+ case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet..
+ break;
+
+ case ($type & DB_DATAOBJECT_BOOL):
+ $defaults[$ar['Field']] = (int)(boolean) $ar['Default'];
+ break;
+
+
+ case ($type & DB_DATAOBJECT_STR):
+ $defaults[$ar['Field']] = "'" . addslashes($ar['Default']) . "'";
+ break;
+
+
+ default: // hopefully eveything else... - numbers etc.
+ if (!strlen($ar['Default'])) {
+ continue;
+ }
+ if (is_numeric($ar['Default'])) {
+ $defaults[$ar['Field']] = $ar['Default'];
+ }
+ break;
+
+ }
+ //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
+ }
+ if (empty($defaults)) {
+ return;
+ }
+
+ $ret = "\n" .
+ " function defaults() // column default values \n" .
+ " {\n" .
+ " return array(\n";
+ foreach($defaults as $k=>$v) {
+ $ret .= ' \''.addslashes($k).'\' => ' . $v . ",\n";
+ }
+ return $ret . " );\n" .
+ " }\n";
+
+
+
+
+ }
+
+
+
+
+
+}
diff --git a/_darcs/pristine/extlib/DB/DataObject/createTables.php b/_darcs/pristine/extlib/DB/DataObject/createTables.php
new file mode 100644
index 000000000..c0659574e
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/DataObject/createTables.php
@@ -0,0 +1,59 @@
+#!/usr/bin/php -q
+<?php
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alan Knowles <alan@akbkhome.com>
+// +----------------------------------------------------------------------+
+//
+// $Id: createTables.php,v 1.24 2006/01/13 01:27:55 alan_k Exp $
+//
+
+// since this version doesnt use overload,
+// and I assume anyone using custom generators should add this..
+
+define('DB_DATAOBJECT_NO_OVERLOAD',1);
+
+//require_once 'DB/DataObject/Generator.php';
+require_once 'DB/DataObject/Generator.php';
+
+if (!ini_get('register_argc_argv')) {
+ PEAR::raiseError("\nERROR: You must turn register_argc_argv On in you php.ini file for this to work\neg.\n\nregister_argc_argv = On\n\n", null, PEAR_ERROR_DIE);
+ exit;
+}
+
+if (!@$_SERVER['argv'][1]) {
+ PEAR::raiseError("\nERROR: createTable.php usage:\n\nC:\php\pear\DB\DataObjects\createTable.php example.ini\n\n", null, PEAR_ERROR_DIE);
+ exit;
+}
+
+$config = parse_ini_file($_SERVER['argv'][1], true);
+foreach($config as $class=>$values) {
+ $options = &PEAR::getStaticProperty($class,'options');
+ $options = $values;
+}
+
+
+$options = &PEAR::getStaticProperty('DB_DataObject','options');
+if (empty($options)) {
+ PEAR::raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE);
+ exit;
+}
+set_time_limit(0);
+
+// use debug level from file if set..
+DB_DataObject::debugLevel(isset($options['debug']) ? $options['debug'] : 1);
+
+$generator = new DB_DataObject_Generator;
+$generator->start();
+
diff --git a/_darcs/pristine/extlib/DB/common.php b/_darcs/pristine/extlib/DB/common.php
new file mode 100644
index 000000000..c51323d25
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/common.php
@@ -0,0 +1,2262 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Contains the DB_common base class
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: common.php,v 1.144 2007/11/26 22:54:03 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the PEAR class so it can be extended from
+ */
+require_once 'PEAR.php';
+
+/**
+ * DB_common is the base class from which each database driver class extends
+ *
+ * All common methods are declared here. If a given DBMS driver contains
+ * a particular method, that method will overload the one here.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_common extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * The current default fetch mode
+ * @var integer
+ */
+ var $fetchmode = DB_FETCHMODE_ORDERED;
+
+ /**
+ * The name of the class into which results should be fetched when
+ * DB_FETCHMODE_OBJECT is in effect
+ *
+ * @var string
+ */
+ var $fetchmode_object_class = 'stdClass';
+
+ /**
+ * Was a connection present when the object was serialized()?
+ * @var bool
+ * @see DB_common::__sleep(), DB_common::__wake()
+ */
+ var $was_connected = null;
+
+ /**
+ * The most recently executed query
+ * @var string
+ */
+ var $last_query = '';
+
+ /**
+ * Run-time configuration options
+ *
+ * The 'optimize' option has been deprecated. Use the 'portability'
+ * option instead.
+ *
+ * @var array
+ * @see DB_common::setOption()
+ */
+ var $options = array(
+ 'result_buffering' => 500,
+ 'persistent' => false,
+ 'ssl' => false,
+ 'debug' => 0,
+ 'seqname_format' => '%s_seq',
+ 'autofree' => false,
+ 'portability' => DB_PORTABILITY_NONE,
+ 'optimize' => 'performance', // Deprecated. Use 'portability'.
+ );
+
+ /**
+ * The parameters from the most recently executed query
+ * @var array
+ * @since Property available since Release 1.7.0
+ */
+ var $last_parameters = array();
+
+ /**
+ * The elements from each prepared statement
+ * @var array
+ */
+ var $prepare_tokens = array();
+
+ /**
+ * The data types of the various elements in each prepared statement
+ * @var array
+ */
+ var $prepare_types = array();
+
+ /**
+ * The prepared queries
+ * @var array
+ */
+ var $prepared_queries = array();
+
+ /**
+ * Flag indicating that the last query was a manipulation query.
+ * @access protected
+ * @var boolean
+ */
+ var $_last_query_manip = false;
+
+ /**
+ * Flag indicating that the next query <em>must</em> be a manipulation
+ * query.
+ * @access protected
+ * @var boolean
+ */
+ var $_next_query_manip = false;
+
+
+ // }}}
+ // {{{ DB_common
+
+ /**
+ * This constructor calls <kbd>$this->PEAR('DB_Error')</kbd>
+ *
+ * @return void
+ */
+ function DB_common()
+ {
+ $this->PEAR('DB_Error');
+ }
+
+ // }}}
+ // {{{ __sleep()
+
+ /**
+ * Automatically indicates which properties should be saved
+ * when PHP's serialize() function is called
+ *
+ * @return array the array of properties names that should be saved
+ */
+ function __sleep()
+ {
+ if ($this->connection) {
+ // Don't disconnect(), people use serialize() for many reasons
+ $this->was_connected = true;
+ } else {
+ $this->was_connected = false;
+ }
+ if (isset($this->autocommit)) {
+ return array('autocommit',
+ 'dbsyntax',
+ 'dsn',
+ 'features',
+ 'fetchmode',
+ 'fetchmode_object_class',
+ 'options',
+ 'was_connected',
+ );
+ } else {
+ return array('dbsyntax',
+ 'dsn',
+ 'features',
+ 'fetchmode',
+ 'fetchmode_object_class',
+ 'options',
+ 'was_connected',
+ );
+ }
+ }
+
+ // }}}
+ // {{{ __wakeup()
+
+ /**
+ * Automatically reconnects to the database when PHP's unserialize()
+ * function is called
+ *
+ * The reconnection attempt is only performed if the object was connected
+ * at the time PHP's serialize() function was run.
+ *
+ * @return void
+ */
+ function __wakeup()
+ {
+ if ($this->was_connected) {
+ $this->connect($this->dsn, $this->options);
+ }
+ }
+
+ // }}}
+ // {{{ __toString()
+
+ /**
+ * Automatic string conversion for PHP 5
+ *
+ * @return string a string describing the current PEAR DB object
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function __toString()
+ {
+ $info = strtolower(get_class($this));
+ $info .= ': (phptype=' . $this->phptype .
+ ', dbsyntax=' . $this->dbsyntax .
+ ')';
+ if ($this->connection) {
+ $info .= ' [connected]';
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * DEPRECATED: String conversion method
+ *
+ * @return string a string describing the current PEAR DB object
+ *
+ * @deprecated Method deprecated in Release 1.7.0
+ */
+ function toString()
+ {
+ return $this->__toString();
+ }
+
+ // }}}
+ // {{{ quoteString()
+
+ /**
+ * DEPRECATED: Quotes a string so it can be safely used within string
+ * delimiters in a query
+ *
+ * @param string $string the string to be quoted
+ *
+ * @return string the quoted string
+ *
+ * @see DB_common::quoteSmart(), DB_common::escapeSimple()
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function quoteString($string)
+ {
+ $string = $this->quote($string);
+ if ($string{0} == "'") {
+ return substr($string, 1, -1);
+ }
+ return $string;
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * DEPRECATED: Quotes a string so it can be safely used in a query
+ *
+ * @param string $string the string to quote
+ *
+ * @return string the quoted string or the string <samp>NULL</samp>
+ * if the value submitted is <kbd>null</kbd>.
+ *
+ * @see DB_common::quoteSmart(), DB_common::escapeSimple()
+ * @deprecated Deprecated in release 1.6.0
+ */
+ function quote($string = null)
+ {
+ return ($string === null) ? 'NULL'
+ : "'" . str_replace("'", "''", $string) . "'";
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * Delimiting style depends on which database driver is being used.
+ *
+ * NOTE: just because you CAN use delimited identifiers doesn't mean
+ * you SHOULD use them. In general, they end up causing way more
+ * problems than they solve.
+ *
+ * Portability is broken by using the following characters inside
+ * delimited identifiers:
+ * + backtick (<kbd>`</kbd>) -- due to MySQL
+ * + double quote (<kbd>"</kbd>) -- due to Oracle
+ * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
+ *
+ * Delimited identifiers are known to generally work correctly under
+ * the following drivers:
+ * + mssql
+ * + mysql
+ * + mysqli
+ * + oci8
+ * + odbc(access)
+ * + odbc(db2)
+ * + pgsql
+ * + sqlite
+ * + sybase (must execute <kbd>set quoted_identifier on</kbd> sometime
+ * prior to use)
+ *
+ * InterBase doesn't seem to be able to use delimited identifiers
+ * via PHP 4. They work fine under PHP 5.
+ *
+ * @param string $str the identifier name to be quoted
+ *
+ * @return string the quoted identifier
+ *
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '"' . str_replace('"', '""', $str) . '"';
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * The output depends on the PHP data type of input and the database
+ * type being used.
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * <ul>
+ * <li>
+ * <kbd>input</kbd> -> <samp>returns</samp>
+ * </li>
+ * <li>
+ * <kbd>null</kbd> -> the string <samp>NULL</samp>
+ * </li>
+ * <li>
+ * <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
+ * </li>
+ * <li>
+ * <kbd>bool</kbd> -> output depends on the driver in use
+ * Most drivers return integers: <samp>1</samp> if
+ * <kbd>true</kbd> or <samp>0</samp> if
+ * <kbd>false</kbd>.
+ * Some return strings: <samp>TRUE</samp> if
+ * <kbd>true</kbd> or <samp>FALSE</samp> if
+ * <kbd>false</kbd>.
+ * Finally one returns strings: <samp>T</samp> if
+ * <kbd>true</kbd> or <samp>F</samp> if
+ * <kbd>false</kbd>. Here is a list of each DBMS,
+ * the values returned and the suggested column type:
+ * <ul>
+ * <li>
+ * <kbd>dbase</kbd> -> <samp>T/F</samp>
+ * (<kbd>Logical</kbd>)
+ * </li>
+ * <li>
+ * <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
+ * (<kbd>BOOLEAN</kbd>)
+ * </li>
+ * <li>
+ * <kbd>ibase</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>ifx</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>msql</kbd> -> <samp>1/0</samp>
+ * (<kbd>INTEGER</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mssql</kbd> -> <samp>1/0</samp>
+ * (<kbd>BIT</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mysql</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mysqli</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>oci8</kbd> -> <samp>1/0</samp>
+ * (<kbd>NUMBER(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>odbc</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
+ * (<kbd>BOOLEAN</kbd>)
+ * </li>
+ * <li>
+ * <kbd>sqlite</kbd> -> <samp>1/0</samp>
+ * (<kbd>INTEGER</kbd>)
+ * </li>
+ * <li>
+ * <kbd>sybase</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * </ul>
+ * [1] Accommodate the lowest common denominator because not all
+ * versions of have <kbd>BOOLEAN</kbd>.
+ * </li>
+ * <li>
+ * other (including strings and numeric strings) ->
+ * the data with single quotes escaped by preceeding
+ * single quotes, backslashes are escaped by preceeding
+ * backslashes, then the whole string is encapsulated
+ * between single quotes
+ * </li>
+ * </ul>
+ *
+ * @see DB_common::escapeSimple()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in)) {
+ return $in;
+ } elseif (is_float($in)) {
+ return $this->quoteFloat($in);
+ } elseif (is_bool($in)) {
+ return $this->quoteBoolean($in);
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ if ($this->dbsyntax == 'access'
+ && preg_match('/^#.+#$/', $in))
+ {
+ return $this->escapeSimple($in);
+ }
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ quoteBoolean()
+
+ /**
+ * Formats a boolean value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param boolean the boolean value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteBoolean($boolean) {
+ return $boolean ? '1' : '0';
+ }
+
+ // }}}
+ // {{{ quoteFloat()
+
+ /**
+ * Formats a float value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param float the float value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteFloat($float) {
+ return "'".$this->escapeSimple(str_replace(',', '.', strval(floatval($float))))."'";
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * In SQLite, this makes things safe for inserts/updates, but may
+ * cause problems when performing text comparisons against columns
+ * containing binary data. See the
+ * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return str_replace("'", "''", $str);
+ }
+
+ // }}}
+ // {{{ provides()
+
+ /**
+ * Tells whether the present driver supports a given feature
+ *
+ * @param string $feature the feature you're curious about
+ *
+ * @return bool whether this driver supports $feature
+ */
+ function provides($feature)
+ {
+ return $this->features[$feature];
+ }
+
+ // }}}
+ // {{{ setFetchMode()
+
+ /**
+ * Sets the fetch mode that should be used by default for query results
+ *
+ * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC
+ * or DB_FETCHMODE_OBJECT
+ * @param string $object_class the class name of the object to be returned
+ * by the fetch methods when the
+ * DB_FETCHMODE_OBJECT mode is selected.
+ * If no class is specified by default a cast
+ * to object from the assoc array row will be
+ * done. There is also the posibility to use
+ * and extend the 'DB_row' class.
+ *
+ * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
+ */
+ function setFetchMode($fetchmode, $object_class = 'stdClass')
+ {
+ switch ($fetchmode) {
+ case DB_FETCHMODE_OBJECT:
+ $this->fetchmode_object_class = $object_class;
+ case DB_FETCHMODE_ORDERED:
+ case DB_FETCHMODE_ASSOC:
+ $this->fetchmode = $fetchmode;
+ break;
+ default:
+ return $this->raiseError('invalid fetchmode mode');
+ }
+ }
+
+ // }}}
+ // {{{ setOption()
+
+ /**
+ * Sets run-time configuration options for PEAR DB
+ *
+ * Options, their data types, default values and description:
+ * <ul>
+ * <li>
+ * <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />should results be freed automatically when there are no
+ * more rows?
+ * </li><li>
+ * <var>result_buffering</var> <kbd>integer</kbd> = <samp>500</samp>
+ * <br />how many rows of the result set should be buffered?
+ * <br />In mysql: mysql_unbuffered_query() is used instead of
+ * mysql_query() if this value is 0. (Release 1.7.0)
+ * <br />In oci8: this value is passed to ocisetprefetch().
+ * (Release 1.7.0)
+ * </li><li>
+ * <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
+ * <br />debug level
+ * </li><li>
+ * <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />should the connection be persistent?
+ * </li><li>
+ * <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
+ * <br />portability mode constant (see below)
+ * </li><li>
+ * <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
+ * <br />the sprintf() format string used on sequence names. This
+ * format is applied to sequence names passed to
+ * createSequence(), nextID() and dropSequence().
+ * </li><li>
+ * <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />use ssl to connect?
+ * </li>
+ * </ul>
+ *
+ * -----------------------------------------
+ *
+ * PORTABILITY MODES
+ *
+ * These modes are bitwised, so they can be combined using <kbd>|</kbd>
+ * and removed using <kbd>^</kbd>. See the examples section below on how
+ * to do this.
+ *
+ * <samp>DB_PORTABILITY_NONE</samp>
+ * turn off all portability features
+ *
+ * This mode gets automatically turned on if the deprecated
+ * <var>optimize</var> option gets set to <samp>performance</samp>.
+ *
+ *
+ * <samp>DB_PORTABILITY_LOWERCASE</samp>
+ * convert names of tables and fields to lower case when using
+ * <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + oci8
+ *
+ *
+ * <samp>DB_PORTABILITY_RTRIM</samp>
+ * right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
+ *
+ *
+ * <samp>DB_PORTABILITY_DELETE_COUNT</samp>
+ * force reporting the number of rows deleted
+ *
+ * Some DBMS's don't count the number of rows deleted when performing
+ * simple <kbd>DELETE FROM tablename</kbd> queries. This portability
+ * mode tricks such DBMS's into telling the count by adding
+ * <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + fbsql
+ * + mysql
+ * + mysqli
+ * + sqlite
+ *
+ *
+ * <samp>DB_PORTABILITY_NUMROWS</samp>
+ * enable hack that makes <kbd>numRows()</kbd> work in Oracle
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + oci8
+ *
+ *
+ * <samp>DB_PORTABILITY_ERRORS</samp>
+ * makes certain error messages in certain drivers compatible
+ * with those from other DBMS's
+ *
+ * + mysql, mysqli: change unique/primary key constraints
+ * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
+ *
+ * + odbc(access): MS's ODBC driver reports 'no such field' as code
+ * 07001, which means 'too few parameters.' When this option is on
+ * that code gets mapped to DB_ERROR_NOSUCHFIELD.
+ * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
+ *
+ * <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
+ * convert null values to empty strings in data output by get*() and
+ * fetch*(). Needed because Oracle considers empty strings to be null,
+ * while most other DBMS's know the difference between empty and null.
+ *
+ *
+ * <samp>DB_PORTABILITY_ALL</samp>
+ * turn on all portability features
+ *
+ * -----------------------------------------
+ *
+ * Example 1. Simple setOption() example
+ * <code>
+ * $db->setOption('autofree', true);
+ * </code>
+ *
+ * Example 2. Portability for lowercasing and trimming
+ * <code>
+ * $db->setOption('portability',
+ * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
+ * </code>
+ *
+ * Example 3. All portability options except trimming
+ * <code>
+ * $db->setOption('portability',
+ * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
+ * </code>
+ *
+ * @param string $option option name
+ * @param mixed $value value for the option
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::$options
+ */
+ function setOption($option, $value)
+ {
+ if (isset($this->options[$option])) {
+ $this->options[$option] = $value;
+
+ /*
+ * Backwards compatibility check for the deprecated 'optimize'
+ * option. Done here in case settings change after connecting.
+ */
+ if ($option == 'optimize') {
+ if ($value == 'portability') {
+ switch ($this->phptype) {
+ case 'oci8':
+ $this->options['portability'] =
+ DB_PORTABILITY_LOWERCASE |
+ DB_PORTABILITY_NUMROWS;
+ break;
+ case 'fbsql':
+ case 'mysql':
+ case 'mysqli':
+ case 'sqlite':
+ $this->options['portability'] =
+ DB_PORTABILITY_DELETE_COUNT;
+ break;
+ }
+ } else {
+ $this->options['portability'] = DB_PORTABILITY_NONE;
+ }
+ }
+
+ return DB_OK;
+ }
+ return $this->raiseError("unknown option $option");
+ }
+
+ // }}}
+ // {{{ getOption()
+
+ /**
+ * Returns the value of an option
+ *
+ * @param string $option the option name you're curious about
+ *
+ * @return mixed the option's value
+ */
+ function getOption($option)
+ {
+ if (isset($this->options[$option])) {
+ return $this->options[$option];
+ }
+ return $this->raiseError("unknown option $option");
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute()
+ *
+ * Creates a query that can be run multiple times. Each time it is run,
+ * the placeholders, if any, will be replaced by the contents of
+ * execute()'s $data argument.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> scalar value (i.e. strings, integers). The system
+ * will automatically quote and escape the data.
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Example 1.
+ * <code>
+ * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
+ * $data = array(
+ * "John's text",
+ * "'it''s good'",
+ * 'filename.txt'
+ * );
+ * $res = $db->execute($sth, $data);
+ * </code>
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders:
+ * <pre>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </pre>
+ *
+ * With some database backends, this is emulated.
+ *
+ * {@internal ibase and oci8 have their own prepare() methods.}}
+ *
+ * @param string $query the query to be prepared
+ *
+ * @return mixed DB statement resource on success. A DB_Error object
+ * on failure.
+ *
+ * @see DB_common::execute()
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $token = 0;
+ $types = array();
+ $newtokens = array();
+
+ foreach ($tokens as $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ break;
+ default:
+ $newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ }
+ }
+
+ $this->prepare_tokens[] = &$newtokens;
+ end($this->prepare_tokens);
+
+ $k = key($this->prepare_tokens);
+ $this->prepare_types[$k] = $types;
+ $this->prepared_queries[$k] = implode(' ', $newtokens);
+
+ return $k;
+ }
+
+ // }}}
+ // {{{ autoPrepare()
+
+ /**
+ * Automaticaly generates an insert or update query and pass it to prepare()
+ *
+ * @param string $table the table name
+ * @param array $table_fields the array of field names
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return resource the query handle
+ *
+ * @uses DB_common::prepare(), DB_common::buildManipSQL()
+ */
+ function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT,
+ $where = false)
+ {
+ $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
+ if (DB::isError($query)) {
+ return $query;
+ }
+ return $this->prepare($query);
+ }
+
+ // }}}
+ // {{{ autoExecute()
+
+ /**
+ * Automaticaly generates an insert or update query and call prepare()
+ * and execute() with it
+ *
+ * @param string $table the table name
+ * @param array $fields_values the associative array where $key is a
+ * field name and $value its value
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * @uses DB_common::autoPrepare(), DB_common::execute()
+ */
+ function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT,
+ $where = false)
+ {
+ $sth = $this->autoPrepare($table, array_keys($fields_values), $mode,
+ $where);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $ret = $this->execute($sth, array_values($fields_values));
+ $this->freePrepared($sth);
+ return $ret;
+
+ }
+
+ // }}}
+ // {{{ buildManipSQL()
+
+ /**
+ * Produces an SQL query string for autoPrepare()
+ *
+ * Example:
+ * <pre>
+ * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
+ * DB_AUTOQUERY_INSERT);
+ * </pre>
+ *
+ * That returns
+ * <samp>
+ * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
+ * </samp>
+ *
+ * NOTES:
+ * - This belongs more to a SQL Builder class, but this is a simple
+ * facility.
+ * - Be carefull! If you don't give a $where param with an UPDATE
+ * query, all the records of the table will be updated!
+ *
+ * @param string $table the table name
+ * @param array $table_fields the array of field names
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return string the sql query for autoPrepare()
+ */
+ function buildManipSQL($table, $table_fields, $mode, $where = false)
+ {
+ if (count($table_fields) == 0) {
+ return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+ $first = true;
+ switch ($mode) {
+ case DB_AUTOQUERY_INSERT:
+ $values = '';
+ $names = '';
+ foreach ($table_fields as $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $names .= ',';
+ $values .= ',';
+ }
+ $names .= $value;
+ $values .= '?';
+ }
+ return "INSERT INTO $table ($names) VALUES ($values)";
+ case DB_AUTOQUERY_UPDATE:
+ $set = '';
+ foreach ($table_fields as $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $set .= ',';
+ }
+ $set .= "$value = ?";
+ }
+ $sql = "UPDATE $table SET $set";
+ if ($where) {
+ $sql .= " WHERE $where";
+ }
+ return $sql;
+ default:
+ return $this->raiseError(DB_ERROR_SYNTAX);
+ }
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare()
+ *
+ * Example 1.
+ * <code>
+ * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
+ * $data = array(
+ * "John's text",
+ * "'it''s good'",
+ * 'filename.txt'
+ * );
+ * $res = $db->execute($sth, $data);
+ * </code>
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * {@internal ibase and oci8 have their own execute() methods.}}
+ *
+ * @see DB_common::prepare()
+ */
+ function &execute($stmt, $data = array())
+ {
+ $realquery = $this->executeEmulateQuery($stmt, $data);
+ if (DB::isError($realquery)) {
+ return $realquery;
+ }
+ $result = $this->simpleQuery($realquery);
+
+ if ($result === DB_OK || DB::isError($result)) {
+ return $result;
+ } else {
+ $tmp = new DB_result($this, $result);
+ return $tmp;
+ }
+ }
+
+ // }}}
+ // {{{ executeEmulateQuery()
+
+ /**
+ * Emulates executing prepared statements if the DBMS not support them
+ *
+ * @param resource $stmt a DB statement resource returned from execute()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a string containing the real query run when emulating
+ * prepare/execute. A DB_Error object on failure.
+ *
+ * @access protected
+ * @see DB_common::execute()
+ */
+ function executeEmulateQuery($stmt, $data = array())
+ {
+ $stmt = (int)$stmt;
+ $data = (array)$data;
+ $this->last_parameters = $data;
+
+ if (count($this->prepare_types[$stmt]) != count($data)) {
+ $this->last_query = $this->prepared_queries[$stmt];
+ return $this->raiseError(DB_ERROR_MISMATCH);
+ }
+
+ $realquery = $this->prepare_tokens[$stmt][0];
+
+ $i = 0;
+ foreach ($data as $value) {
+ if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
+ $realquery .= $this->quoteSmart($value);
+ } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($value, 'rb');
+ if (!$fp) {
+ return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+ $realquery .= $this->quoteSmart(fread($fp, filesize($value)));
+ fclose($fp);
+ } else {
+ $realquery .= $value;
+ }
+
+ $realquery .= $this->prepare_tokens[$stmt][++$i];
+ }
+
+ return $realquery;
+ }
+
+ // }}}
+ // {{{ executeMultiple()
+
+ /**
+ * Performs several execute() calls on the same statement handle
+ *
+ * $data must be an array indexed numerically
+ * from 0, one execute call is done for every "row" in the array.
+ *
+ * If an error occurs during execute(), executeMultiple() does not
+ * execute the unfinished rows, but rather returns that error.
+ *
+ * @param resource $stmt query handle from prepare()
+ * @param array $data numeric array containing the
+ * data to insert into the query
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::prepare(), DB_common::execute()
+ */
+ function executeMultiple($stmt, $data)
+ {
+ foreach ($data as $value) {
+ $res = $this->execute($stmt, $value);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freePrepared()
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's PHP resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_common::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ $stmt = (int)$stmt;
+ if (isset($this->prepare_tokens[$stmt])) {
+ unset($this->prepare_tokens[$stmt]);
+ unset($this->prepare_types[$stmt]);
+ unset($this->prepared_queries[$stmt]);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * It is defined here to ensure all drivers have this method available.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(),
+ * DB_sqlite::modifyQuery()
+ */
+ function modifyQuery($query)
+ {
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * It is defined here to assure that all implementations
+ * have this method defined.
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return $query;
+ }
+
+ // }}}
+ // {{{ query()
+
+ /**
+ * Sends a query to the database server
+ *
+ * The query string can be either a normal statement to be sent directly
+ * to the server OR if <var>$params</var> are passed the query can have
+ * placeholders and it will be passed through prepare() and execute().
+ *
+ * @param string $query the SQL query or the statement to prepare
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * @see DB_result, DB_common::prepare(), DB_common::execute()
+ */
+ function &query($query, $params = array())
+ {
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $ret = $this->execute($sth, $params);
+ $this->freePrepared($sth, false);
+ return $ret;
+ } else {
+ $this->last_parameters = array();
+ $result = $this->simpleQuery($query);
+ if ($result === DB_OK || DB::isError($result)) {
+ return $result;
+ } else {
+ $tmp = new DB_result($this, $result);
+ return $tmp;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ limitQuery()
+
+ /**
+ * Generates and executes a LIMIT query
+ *
+ * @param string $query the query
+ * @param intr $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ */
+ function &limitQuery($query, $from, $count, $params = array())
+ {
+ $query = $this->modifyLimitQuery($query, $from, $count, $params);
+ if (DB::isError($query)){
+ return $query;
+ }
+ $result = $this->query($query, $params);
+ if (is_a($result, 'DB_result')) {
+ $result->setOption('limit_from', $from);
+ $result->setOption('limit_count', $count);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Fetches the first column of the first row from a query result
+ *
+ * Takes care of doing the query and freeing the results when finished.
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed the returned value of the query.
+ * A DB_Error object on failure.
+ */
+ function &getOne($query, $params = array())
+ {
+ $params = (array)$params;
+ // modifyLimitQuery() would be nice here, but it causes BC issues
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $res = $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res = $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
+ $res->free();
+
+ if ($err !== DB_OK) {
+ return $err;
+ }
+
+ return $row[0];
+ }
+
+ // }}}
+ // {{{ getRow()
+
+ /**
+ * Fetches the first row of data returned from a query result
+ *
+ * Takes care of doing the query and freeing the results when finished.
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use
+ *
+ * @return array the first row of results as an array.
+ * A DB_Error object on failure.
+ */
+ function &getRow($query, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT)
+ {
+ // compat check, the params and fetchmode parameters used to
+ // have the opposite order
+ if (!is_array($params)) {
+ if (is_array($fetchmode)) {
+ if ($params === null) {
+ $tmp = DB_FETCHMODE_DEFAULT;
+ } else {
+ $tmp = $params;
+ }
+ $params = $fetchmode;
+ $fetchmode = $tmp;
+ } elseif ($params !== null) {
+ $fetchmode = $params;
+ $params = array();
+ }
+ }
+ // modifyLimitQuery() would be nice here, but it causes BC issues
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $res = $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res = $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $err = $res->fetchInto($row, $fetchmode);
+
+ $res->free();
+
+ if ($err !== DB_OK) {
+ return $err;
+ }
+
+ return $row;
+ }
+
+ // }}}
+ // {{{ getCol()
+
+ /**
+ * Fetches a single column from a query result and returns it as an
+ * indexed array
+ *
+ * @param string $query the SQL query
+ * @param mixed $col which column to return (integer [column number,
+ * starting at 0] or string [column name])
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return array the results as an array. A DB_Error object on failure.
+ *
+ * @see DB_common::query()
+ */
+ function &getCol($query, $col = 0, $params = array())
+ {
+ $params = (array)$params;
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res = $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res = $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
+
+ if (!is_array($row = $res->fetchRow($fetchmode))) {
+ $ret = array();
+ } else {
+ if (!array_key_exists($col, $row)) {
+ $ret = $this->raiseError(DB_ERROR_NOSUCHFIELD);
+ } else {
+ $ret = array($row[$col]);
+ while (is_array($row = $res->fetchRow($fetchmode))) {
+ $ret[] = $row[$col];
+ }
+ }
+ }
+
+ $res->free();
+
+ if (DB::isError($row)) {
+ $ret = $row;
+ }
+
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getAssoc()
+
+ /**
+ * Fetches an entire query result and returns it as an
+ * associative array using the first column as the key
+ *
+ * If the result set contains more than two columns, the value
+ * will be an array of the values from column 2-n. If the result
+ * set contains only two columns, the returned value will be a
+ * scalar with the value of the second column (unless forced to an
+ * array with the $force_array parameter). A DB error code is
+ * returned on errors. If the result set contains fewer than two
+ * columns, a DB_ERROR_TRUNCATED error is returned.
+ *
+ * For example, if the table "mytable" contains:
+ *
+ * <pre>
+ * ID TEXT DATE
+ * --------------------------------
+ * 1 'one' 944679408
+ * 2 'two' 944679408
+ * 3 'three' 944679408
+ * </pre>
+ *
+ * Then the call getAssoc('SELECT id,text FROM mytable') returns:
+ * <pre>
+ * array(
+ * '1' => 'one',
+ * '2' => 'two',
+ * '3' => 'three',
+ * )
+ * </pre>
+ *
+ * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
+ * <pre>
+ * array(
+ * '1' => array('one', '944679408'),
+ * '2' => array('two', '944679408'),
+ * '3' => array('three', '944679408')
+ * )
+ * </pre>
+ *
+ * If the more than one row occurs with the same value in the
+ * first column, the last row overwrites all previous ones by
+ * default. Use the $group parameter if you don't want to
+ * overwrite like this. Example:
+ *
+ * <pre>
+ * getAssoc('SELECT category,id,name FROM mytable', false, null,
+ * DB_FETCHMODE_ASSOC, true) returns:
+ *
+ * array(
+ * '1' => array(array('id' => '4', 'name' => 'number four'),
+ * array('id' => '6', 'name' => 'number six')
+ * ),
+ * '9' => array(array('id' => '4', 'name' => 'number four'),
+ * array('id' => '6', 'name' => 'number six')
+ * )
+ * )
+ * </pre>
+ *
+ * Keep in mind that database functions in PHP usually return string
+ * values for results regardless of the database's internal type.
+ *
+ * @param string $query the SQL query
+ * @param bool $force_array used only when the query returns
+ * exactly two columns. If true, the values
+ * of the returned array will be one-element
+ * arrays instead of scalars.
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of
+ * items passed must match quantity of
+ * placeholders in query: meaning 1
+ * placeholder for non-array parameters or
+ * 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use
+ * @param bool $group if true, the values of the returned array
+ * is wrapped in another array. If the same
+ * key value (in the first column) repeats
+ * itself, the values will be appended to
+ * this array instead of overwriting the
+ * existing values.
+ *
+ * @return array the associative array containing the query results.
+ * A DB_Error object on failure.
+ */
+ function &getAssoc($query, $force_array = false, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
+ {
+ $params = (array)$params;
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res = $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res = $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+ if ($fetchmode == DB_FETCHMODE_DEFAULT) {
+ $fetchmode = $this->fetchmode;
+ }
+ $cols = $res->numCols();
+
+ if ($cols < 2) {
+ $tmp = $this->raiseError(DB_ERROR_TRUNCATED);
+ return $tmp;
+ }
+
+ $results = array();
+
+ if ($cols > 2 || $force_array) {
+ // return array values
+ // XXX this part can be optimized
+ if ($fetchmode == DB_FETCHMODE_ASSOC) {
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
+ reset($row);
+ $key = current($row);
+ unset($row[key($row)]);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ } elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
+ while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
+ $arr = get_object_vars($row);
+ $key = current($arr);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ } else {
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
+ // we shift away the first element to get
+ // indices running from 0 again
+ $key = array_shift($row);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ }
+ if (DB::isError($row)) {
+ $results = $row;
+ }
+ } else {
+ // return scalar values
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
+ if ($group) {
+ $results[$row[0]][] = $row[1];
+ } else {
+ $results[$row[0]] = $row[1];
+ }
+ }
+ if (DB::isError($row)) {
+ $results = $row;
+ }
+ }
+
+ $res->free();
+
+ return $results;
+ }
+
+ // }}}
+ // {{{ getAll()
+
+ /**
+ * Fetches all of the rows from a query result
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of
+ * items passed must match quantity of
+ * placeholders in query: meaning 1
+ * placeholder for non-array parameters or
+ * 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use:
+ * + DB_FETCHMODE_ORDERED
+ * + DB_FETCHMODE_ASSOC
+ * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
+ * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
+ *
+ * @return array the nested array. A DB_Error object on failure.
+ */
+ function &getAll($query, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT)
+ {
+ // compat check, the params and fetchmode parameters used to
+ // have the opposite order
+ if (!is_array($params)) {
+ if (is_array($fetchmode)) {
+ if ($params === null) {
+ $tmp = DB_FETCHMODE_DEFAULT;
+ } else {
+ $tmp = $params;
+ }
+ $params = $fetchmode;
+ $fetchmode = $tmp;
+ } elseif ($params !== null) {
+ $fetchmode = $params;
+ $params = array();
+ }
+ }
+
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res = $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res = $this->query($query);
+ }
+
+ if ($res === DB_OK || DB::isError($res)) {
+ return $res;
+ }
+
+ $results = array();
+ while (DB_OK === $res->fetchInto($row, $fetchmode)) {
+ if ($fetchmode & DB_FETCHMODE_FLIPPED) {
+ foreach ($row as $key => $val) {
+ $results[$key][] = $val;
+ }
+ } else {
+ $results[] = $row;
+ }
+ }
+
+ $res->free();
+
+ if (DB::isError($row)) {
+ $tmp = $this->raiseError($row);
+ return $tmp;
+ }
+ return $results;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Determines the number of rows in a query result
+ *
+ * @param resource $result the query result idenifier produced by PHP
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function numRows($result)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ getSequenceName()
+
+ /**
+ * Generates the name used inside the database for a sequence
+ *
+ * The createSequence() docblock contains notes about storing sequence
+ * names.
+ *
+ * @param string $sqn the sequence's public name
+ *
+ * @return string the sequence's name in the backend
+ *
+ * @access protected
+ * @see DB_common::createSequence(), DB_common::dropSequence(),
+ * DB_common::nextID(), DB_common::setOption()
+ */
+ function getSequenceName($sqn)
+ {
+ return sprintf($this->getOption('seqname_format'),
+ preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::dropSequence(),
+ * DB_common::getSequenceName()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * The name of a given sequence is determined by passing the string
+ * provided in the <var>$seq_name</var> argument through PHP's sprintf()
+ * function using the value from the <var>seqname_format</var> option as
+ * the sprintf()'s format argument.
+ *
+ * <var>seqname_format</var> is set via setOption().
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_common::nextID()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_common::nextID()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * Communicates an error and invoke error callbacks, etc
+ *
+ * Basically a wrapper for PEAR::raiseError without the message string.
+ *
+ * @param mixed integer error code, or a PEAR error object (all
+ * other parameters are ignored if this parameter is
+ * an object
+ * @param int error mode, see PEAR_Error docs
+ * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the
+ * error level (E_USER_NOTICE etc). If error mode is
+ * PEAR_ERROR_CALLBACK, this is the callback function,
+ * either as a function name, or as an array of an
+ * object and method name. For other error modes this
+ * parameter is ignored.
+ * @param string extra debug information. Defaults to the last
+ * query and native error code.
+ * @param mixed native error code, integer or string depending the
+ * backend
+ * @param mixed dummy parameter for E_STRICT compatibility with
+ * PEAR::raiseError
+ * @param mixed dummy parameter for E_STRICT compatibility with
+ * PEAR::raiseError
+ *
+ * @return object the PEAR_Error object
+ *
+ * @see PEAR_Error
+ */
+ function &raiseError($code = DB_ERROR, $mode = null, $options = null,
+ $userinfo = null, $nativecode = null, $dummy1 = null,
+ $dummy2 = null)
+ {
+ // The error is yet a DB error object
+ if (is_object($code)) {
+ // because we the static PEAR::raiseError, our global
+ // handler should be used if it is set
+ if ($mode === null && !empty($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ }
+ $tmp = PEAR::raiseError($code, null, $mode, $options,
+ null, null, true);
+ return $tmp;
+ }
+
+ if ($userinfo === null) {
+ $userinfo = $this->last_query;
+ }
+
+ if ($nativecode) {
+ $userinfo .= ' [nativecode=' . trim($nativecode) . ']';
+ } else {
+ $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']';
+ }
+
+ $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
+ 'DB_Error', true);
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return mixed the DBMS' error code. A DB_Error object on failure.
+ */
+ function errorNative()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones
+ *
+ * Uses the <var>$errorcode_map</var> property defined in each driver.
+ *
+ * @param string|int $nativecode the error code returned by the DBMS
+ *
+ * @return int the portable DB error code. Return DB_ERROR if the
+ * current driver doesn't have a mapping for the
+ * $nativecode submitted.
+ */
+ function errorCode($nativecode)
+ {
+ if (isset($this->errorcode_map[$nativecode])) {
+ return $this->errorcode_map[$nativecode];
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ errorMessage()
+
+ /**
+ * Maps a DB error code to a textual message
+ *
+ * @param integer $dbcode the DB error code
+ *
+ * @return string the error message corresponding to the error code
+ * submitted. FALSE if the error code is unknown.
+ *
+ * @see DB::errorMessage()
+ */
+ function errorMessage($dbcode)
+ {
+ return DB::errorMessage($this->errorcode_map[$dbcode]);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * The format of the resulting array depends on which <var>$mode</var>
+ * you select. The sample output below is based on this query:
+ * <pre>
+ * SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
+ * FROM tblFoo
+ * JOIN tblBar ON tblFoo.fldId = tblBar.fldId
+ * </pre>
+ *
+ * <ul>
+ * <li>
+ *
+ * <kbd>null</kbd> (default)
+ * <pre>
+ * [0] => Array (
+ * [table] => tblFoo
+ * [name] => fldId
+ * [type] => int
+ * [len] => 11
+ * [flags] => primary_key not_null
+ * )
+ * [1] => Array (
+ * [table] => tblFoo
+ * [name] => fldPhone
+ * [type] => string
+ * [len] => 20
+ * [flags] =>
+ * )
+ * [2] => Array (
+ * [table] => tblBar
+ * [name] => fldId
+ * [type] => int
+ * [len] => 11
+ * [flags] => primary_key not_null
+ * )
+ * </pre>
+ *
+ * </li><li>
+ *
+ * <kbd>DB_TABLEINFO_ORDER</kbd>
+ *
+ * <p>In addition to the information found in the default output,
+ * a notation of the number of columns is provided by the
+ * <samp>num_fields</samp> element while the <samp>order</samp>
+ * element provides an array with the column names as the keys and
+ * their location index number (corresponding to the keys in the
+ * the default output) as the values.</p>
+ *
+ * <p>If a result set has identical field names, the last one is
+ * used.</p>
+ *
+ * <pre>
+ * [num_fields] => 3
+ * [order] => Array (
+ * [fldId] => 2
+ * [fldTrans] => 1
+ * )
+ * </pre>
+ *
+ * </li><li>
+ *
+ * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
+ *
+ * <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
+ * dimensions to the array in which the table names are keys and
+ * the field names are sub-keys. This is helpful for queries that
+ * join tables which have identical field names.</p>
+ *
+ * <pre>
+ * [num_fields] => 3
+ * [ordertable] => Array (
+ * [tblFoo] => Array (
+ * [fldId] => 0
+ * [fldPhone] => 1
+ * )
+ * [tblBar] => Array (
+ * [fldId] => 2
+ * )
+ * )
+ * </pre>
+ *
+ * </li>
+ * </ul>
+ *
+ * The <samp>flags</samp> element contains a space separated list
+ * of extra information about the field. This data is inconsistent
+ * between DBMS's due to the way each DBMS works.
+ * + <samp>primary_key</samp>
+ * + <samp>unique_key</samp>
+ * + <samp>multiple_key</samp>
+ * + <samp>not_null</samp>
+ *
+ * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
+ * elements if <var>$result</var> is a table name. The following DBMS's
+ * provide full information from queries:
+ * + fbsql
+ * + mysql
+ *
+ * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
+ * turned on, the names of tables and fields will be lowercased.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode either unused or one of the tableInfo modes:
+ * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
+ * <kbd>DB_TABLEINFO_ORDER</kbd> or
+ * <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
+ * These are bitwise, so the first two can be
+ * combined using <kbd>|</kbd>.
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ /*
+ * If the DB_<driver> class has a tableInfo() method, that one
+ * overrides this one. But, if the driver doesn't have one,
+ * this method runs and tells users about that fact.
+ */
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ getTables()
+
+ /**
+ * Lists the tables in the current database
+ *
+ * @return array the list of tables. A DB_Error object on failure.
+ *
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function getTables()
+ {
+ return $this->getListOf('tables');
+ }
+
+ // }}}
+ // {{{ getListOf()
+
+ /**
+ * Lists internal database information
+ *
+ * @param string $type type of information being sought.
+ * Common items being sought are:
+ * tables, databases, users, views, functions
+ * Each DBMS's has its own capabilities.
+ *
+ * @return array an array listing the items sought.
+ * A DB DB_Error object on failure.
+ */
+ function getListOf($type)
+ {
+ $sql = $this->getSpecialQuery($type);
+ if ($sql === null) {
+ $this->last_query = '';
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ } elseif (is_int($sql) || DB::isError($sql)) {
+ // Previous error
+ return $this->raiseError($sql);
+ } elseif (is_array($sql)) {
+ // Already the result
+ return $sql;
+ }
+ // Launch this query
+ return $this->getCol($sql);
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ }
+
+ // }}}
+ // {{{ nextQueryIsManip()
+
+ /**
+ * Sets (or unsets) a flag indicating that the next query will be a
+ * manipulation query, regardless of the usual DB::isManip() heuristics.
+ *
+ * @param boolean true to set the flag overriding the isManip() behaviour,
+ * false to clear it and fall back onto isManip()
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function nextQueryIsManip($manip)
+ {
+ $this->_next_query_manip = $manip;
+ }
+
+ // }}}
+ // {{{ _checkManip()
+
+ /**
+ * Checks if the given query is a manipulation query. This also takes into
+ * account the _next_query_manip flag and sets the _last_query_manip flag
+ * (and resets _next_query_manip) according to the result.
+ *
+ * @param string The query to check.
+ *
+ * @return boolean true if the query is a manipulation query, false
+ * otherwise
+ *
+ * @access protected
+ */
+ function _checkManip($query)
+ {
+ if ($this->_next_query_manip || DB::isManip($query)) {
+ $this->_last_query_manip = true;
+ } else {
+ $this->_last_query_manip = false;
+ }
+ $this->_next_query_manip = false;
+ return $this->_last_query_manip;
+ $manip = $this->_next_query_manip;
+ }
+
+ // }}}
+ // {{{ _rtrimArrayValues()
+
+ /**
+ * Right-trims all strings in an array
+ *
+ * @param array $array the array to be trimmed (passed by reference)
+ *
+ * @return void
+ *
+ * @access protected
+ */
+ function _rtrimArrayValues(&$array)
+ {
+ foreach ($array as $key => $value) {
+ if (is_string($value)) {
+ $array[$key] = rtrim($value);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _convertNullArrayValuesToEmpty()
+
+ /**
+ * Converts all null values in an array to empty strings
+ *
+ * @param array $array the array to be de-nullified (passed by reference)
+ *
+ * @return void
+ *
+ * @access protected
+ */
+ function _convertNullArrayValuesToEmpty(&$array)
+ {
+ foreach ($array as $key => $value) {
+ if (is_null($value)) {
+ $array[$key] = '';
+ }
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/dbase.php b/_darcs/pristine/extlib/DB/dbase.php
new file mode 100644
index 000000000..67afc897d
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/dbase.php
@@ -0,0 +1,510 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's dbase extension
+ * for interacting with dBase databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: dbase.php,v 1.45 2007/09/21 13:40:41 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's dbase extension
+ * for interacting with dBase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_dbase extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'dbase';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'dbase';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => false,
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => false,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * A means of emulating result resources
+ * @var array
+ */
+ var $res_row = array();
+
+ /**
+ * The quantity of results so far
+ *
+ * For emulating result resources.
+ *
+ * @var integer
+ */
+ var $result = 0;
+
+ /**
+ * Maps dbase data type id's to human readable strings
+ *
+ * The human readable values are based on the output of PHP's
+ * dbase_get_header_info() function.
+ *
+ * @var array
+ * @since Property available since Release 1.7.0
+ */
+ var $types = array(
+ 'C' => 'character',
+ 'D' => 'date',
+ 'L' => 'boolean',
+ 'M' => 'memo',
+ 'N' => 'number',
+ );
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_dbase()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database and create it if it doesn't exist
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's dbase driver supports the following extra DSN options:
+ * + mode An integer specifying the read/write mode to use
+ * (0 = read only, 1 = write only, 2 = read/write).
+ * Available since PEAR DB 1.7.0.
+ * + fields An array of arrays that PHP's dbase_create() function needs
+ * to create a new database. This information is used if the
+ * dBase file specified in the "database" segment of the DSN
+ * does not exist. For more info, see the PHP manual's
+ * {@link http://php.net/dbase_create dbase_create()} page.
+ * Available since PEAR DB 1.7.0.
+ *
+ * Example of how to connect and establish a new dBase file if necessary:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = array(
+ * 'phptype' => 'dbase',
+ * 'database' => '/path/and/name/of/dbase/file',
+ * 'mode' => 2,
+ * 'fields' => array(
+ * array('a', 'N', 5, 0),
+ * array('b', 'C', 40),
+ * array('c', 'C', 255),
+ * array('d', 'C', 20),
+ * ),
+ * );
+ * $options = array(
+ * 'debug' => 2,
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db = DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('dbase')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ /*
+ * Turn track_errors on for entire script since $php_errormsg
+ * is the only way to find errors from the dbase extension.
+ */
+ @ini_set('track_errors', 1);
+ $php_errormsg = '';
+
+ if (!file_exists($dsn['database'])) {
+ $this->dsn['mode'] = 2;
+ if (empty($dsn['fields']) || !is_array($dsn['fields'])) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ 'the dbase file does not exist and '
+ . 'it could not be created because '
+ . 'the "fields" element of the DSN '
+ . 'is not properly set');
+ }
+ $this->connection = @dbase_create($dsn['database'],
+ $dsn['fields']);
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ 'the dbase file does not exist and '
+ . 'the attempt to create it failed: '
+ . $php_errormsg);
+ }
+ } else {
+ if (!isset($this->dsn['mode'])) {
+ $this->dsn['mode'] = 0;
+ }
+ $this->connection = @dbase_open($dsn['database'],
+ $this->dsn['mode']);
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @dbase_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ &query()
+
+ function &query($query = null)
+ {
+ // emulate result resources
+ $this->res_row[(int)$this->result] = 0;
+ $tmp = new DB_result($this, $this->result++);
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum === null) {
+ $rownum = $this->res_row[(int)$result]++;
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @dbase_get_record_with_names($this->connection, $rownum);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @dbase_get_record($this->connection, $rownum);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set.
+ *
+ * This method is a no-op for dbase, as there aren't result resources in
+ * the same sense as most other database backends.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return true;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($foo)
+ {
+ return @dbase_numfields($this->connection);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($foo)
+ {
+ return @dbase_numrecords($this->connection);
+ }
+
+ // }}}
+ // {{{ quoteBoolean()
+
+ /**
+ * Formats a boolean value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param boolean the boolean value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteBoolean($boolean) {
+ return $boolean ? 'T' : 'F';
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about the current database
+ *
+ * @param mixed $result THIS IS UNUSED IN DBASE. The current database
+ * is examined regardless of what is provided here.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result = null, $mode = null)
+ {
+ if (function_exists('dbase_get_header_info')) {
+ $id = @dbase_get_header_info($this->connection);
+ if (!$id && $php_errormsg) {
+ return $this->raiseError(DB_ERROR,
+ null, null, null,
+ $php_errormsg);
+ }
+ } else {
+ /*
+ * This segment for PHP 4 is loosely based on code by
+ * Hadi Rusiah <deegos@yahoo.com> in the comments on
+ * the dBase reference page in the PHP manual.
+ */
+ $db = @fopen($this->dsn['database'], 'r');
+ if (!$db) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+
+ $id = array();
+ $i = 0;
+
+ $line = fread($db, 32);
+ while (!feof($db)) {
+ $line = fread($db, 32);
+ if (substr($line, 0, 1) == chr(13)) {
+ break;
+ } else {
+ $pos = strpos(substr($line, 0, 10), chr(0));
+ $pos = ($pos == 0 ? 10 : $pos);
+ $id[$i] = array(
+ 'name' => substr($line, 0, $pos),
+ 'type' => $this->types[substr($line, 11, 1)],
+ 'length' => ord(substr($line, 16, 1)),
+ 'precision' => ord(substr($line, 17, 1)),
+ );
+ }
+ $i++;
+ }
+
+ fclose($db);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $res = array();
+ $count = count($id);
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $this->dsn['database'],
+ 'name' => $case_func($id[$i]['name']),
+ 'type' => $id[$i]['type'],
+ 'len' => $id[$i]['length'],
+ 'flags' => ''
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ return $res;
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/fbsql.php b/_darcs/pristine/extlib/DB/fbsql.php
new file mode 100644
index 000000000..4de4078f7
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/fbsql.php
@@ -0,0 +1,769 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's fbsql extension
+ * for interacting with FrontBase databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Frank M. Kromann <frank@frontbase.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: fbsql.php,v 1.88 2007/07/06 05:19:21 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's fbsql extension
+ * for interacting with FrontBase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Frank M. Kromann <frank@frontbase.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ * @since Class functional since Release 1.7.0
+ */
+class DB_fbsql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'fbsql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'fbsql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 22 => DB_ERROR_SYNTAX,
+ 85 => DB_ERROR_ALREADY_EXISTS,
+ 108 => DB_ERROR_SYNTAX,
+ 116 => DB_ERROR_NOSUCHTABLE,
+ 124 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 215 => DB_ERROR_NOSUCHFIELD,
+ 217 => DB_ERROR_INVALID_NUMBER,
+ 226 => DB_ERROR_NOSUCHFIELD,
+ 231 => DB_ERROR_INVALID,
+ 239 => DB_ERROR_TRUNCATED,
+ 251 => DB_ERROR_SYNTAX,
+ 266 => DB_ERROR_NOT_FOUND,
+ 357 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 358 => DB_ERROR_CONSTRAINT,
+ 360 => DB_ERROR_CONSTRAINT,
+ 361 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_fbsql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('fbsql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array(
+ $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
+ $dsn['username'] ? $dsn['username'] : null,
+ $dsn['password'] ? $dsn['password'] : null,
+ );
+
+ $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ @ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ @ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+
+ if ($dsn['database']) {
+ if (!@fbsql_select_db($dsn['database'], $this->connection)) {
+ return $this->fbsqlRaiseError();
+ }
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @fbsql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @fbsql_query("$query;", $this->connection);
+ if (!$result) {
+ return $this->fbsqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if ($this->_checkManip($query)) {
+ return DB_OK;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal fbsql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return @fbsql_next_result($result);
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@fbsql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @fbsql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? fbsql_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff=false)
+ {
+ if ($onoff) {
+ $this->query("SET COMMIT TRUE");
+ } else {
+ $this->query("SET COMMIT FALSE");
+ }
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ @fbsql_commit($this->connection);
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ @fbsql_rollback($this->connection);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @fbsql_num_fields($result);
+ if (!$cols) {
+ return $this->fbsqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @fbsql_num_rows($result);
+ if ($rows === null) {
+ return $this->fbsqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->_last_query_manip) {
+ $result = @fbsql_affected_rows($this->connection);
+ } else {
+ $result = 0;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_fbsql::createSequence(), DB_fbsql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query('SELECT UNIQUE FROM ' . $seqname);
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $result;
+ }
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->fbsqlRaiseError();
+ }
+ $result->fetchInto($tmp, DB_FETCHMODE_ORDERED);
+ return $tmp[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_fbsql::nextID(), DB_fbsql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if ($res) {
+ $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_fbsql::nextID(), DB_fbsql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
+ . ' RESTRICT');
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query) || $this->_next_query_manip) {
+ return preg_replace('/^([\s(])*SELECT/i',
+ "\\1SELECT TOP($count)", $query);
+ } else {
+ return preg_replace('/([\s(])*SELECT/i',
+ "\\1SELECT TOP($from, $count)", $query);
+ }
+ }
+
+ // }}}
+ // {{{ quoteBoolean()
+
+ /**
+ * Formats a boolean value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param boolean the boolean value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteBoolean($boolean) {
+ return $boolean ? 'TRUE' : 'FALSE';
+ }
+
+ // }}}
+ // {{{ quoteFloat()
+
+ /**
+ * Formats a float value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param float the float value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteFloat($float) {
+ return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+ }
+
+ // }}}
+ // {{{ fbsqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_fbsql::errorNative(), DB_common::errorCode()
+ */
+ function fbsqlRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $errno = $this->errorCode(fbsql_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @fbsql_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @fbsql_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @fbsql_list_fields($this->dsn['database'],
+ $result, $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @fbsql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $case_func(@fbsql_field_table($id, $i)),
+ 'name' => $case_func(@fbsql_field_name($id, $i)),
+ 'type' => @fbsql_field_type($id, $i),
+ 'len' => @fbsql_field_len($id, $i),
+ 'flags' => @fbsql_field_flags($id, $i),
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @fbsql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT "table_name" FROM information_schema.tables'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk AND'
+ . ' "table_type" = \'BASE TABLE\''
+ . ' AND "schema_name" = current_schema';
+ case 'views':
+ return 'SELECT "table_name" FROM information_schema.tables'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk AND'
+ . ' "table_type" = \'VIEW\''
+ . ' AND "schema_name" = current_schema';
+ case 'users':
+ return 'SELECT "user_name" from information_schema.users';
+ case 'functions':
+ return 'SELECT "routine_name" FROM'
+ . ' information_schema.psm_routines'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk'
+ . ' AND "routine_kind"=\'FUNCTION\''
+ . ' AND "schema_name" = current_schema';
+ case 'procedures':
+ return 'SELECT "routine_name" FROM'
+ . ' information_schema.psm_routines'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk'
+ . ' AND "routine_kind"=\'PROCEDURE\''
+ . ' AND "schema_name" = current_schema';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/ibase.php b/_darcs/pristine/extlib/DB/ibase.php
new file mode 100644
index 000000000..ee19c5589
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/ibase.php
@@ -0,0 +1,1082 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's interbase extension
+ * for interacting with Interbase and Firebird databases
+ *
+ * While this class works with PHP 4, PHP's InterBase extension is
+ * unstable in PHP 4. Use PHP 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: ibase.php,v 1.116 2007/09/21 13:40:41 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's interbase extension
+ * for interacting with Interbase and Firebird databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * While this class works with PHP 4, PHP's InterBase extension is
+ * unstable in PHP 4. Use PHP 5.
+ *
+ * NOTICE: limitQuery() only works for Firebird.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ * @since Class became stable in Release 1.7.0
+ */
+class DB_ibase extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'ibase';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'ibase';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * NOTE: only firebird supports limit.
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => false,
+ 'new_link' => false,
+ 'numrows' => 'emulate',
+ 'pconnect' => true,
+ 'prepare' => true,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ -104 => DB_ERROR_SYNTAX,
+ -150 => DB_ERROR_ACCESS_VIOLATION,
+ -151 => DB_ERROR_ACCESS_VIOLATION,
+ -155 => DB_ERROR_NOSUCHTABLE,
+ -157 => DB_ERROR_NOSUCHFIELD,
+ -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ -170 => DB_ERROR_MISMATCH,
+ -171 => DB_ERROR_MISMATCH,
+ -172 => DB_ERROR_INVALID,
+ // -204 => // Covers too many errors, need to use regex on msg
+ -205 => DB_ERROR_NOSUCHFIELD,
+ -206 => DB_ERROR_NOSUCHFIELD,
+ -208 => DB_ERROR_INVALID,
+ -219 => DB_ERROR_NOSUCHTABLE,
+ -297 => DB_ERROR_CONSTRAINT,
+ -303 => DB_ERROR_INVALID,
+ -413 => DB_ERROR_INVALID_NUMBER,
+ -530 => DB_ERROR_CONSTRAINT,
+ -551 => DB_ERROR_ACCESS_VIOLATION,
+ -552 => DB_ERROR_ACCESS_VIOLATION,
+ // -607 => // Covers too many errors, need to use regex on msg
+ -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ -803 => DB_ERROR_CONSTRAINT,
+ -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ // -902 => // Covers too many errors, need to use regex on msg
+ -904 => DB_ERROR_CONNECT_FAILED,
+ -922 => DB_ERROR_NOSUCHDB,
+ -923 => DB_ERROR_CONNECT_FAILED,
+ -924 => DB_ERROR_CONNECT_FAILED
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ * @access private
+ */
+ var $affected = 0;
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The prepared statement handle from the most recently executed statement
+ *
+ * {@internal Mainly here because the InterBase/Firebird API is only
+ * able to retrieve data from result sets if the statemnt handle is
+ * still in scope.}}
+ *
+ * @var resource
+ */
+ var $last_stmt;
+
+ /**
+ * Is the given prepared statement a data manipulation query?
+ * @var array
+ * @access private
+ */
+ var $manip_query = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_ibase()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's ibase driver supports the following extra DSN options:
+ * + buffers The number of database buffers to allocate for the
+ * server-side cache.
+ * + charset The default character set for a database.
+ * + dialect The default SQL dialect for any statement
+ * executed within a connection. Defaults to the
+ * highest one supported by client libraries.
+ * Functional only with InterBase 6 and up.
+ * + role Functional only with InterBase 5 and up.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('interbase')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+ if ($this->dbsyntax == 'firebird') {
+ $this->features['limit'] = 'alter';
+ }
+
+ $params = array(
+ $dsn['hostspec']
+ ? ($dsn['hostspec'] . ':' . $dsn['database'])
+ : $dsn['database'],
+ $dsn['username'] ? $dsn['username'] : null,
+ $dsn['password'] ? $dsn['password'] : null,
+ isset($dsn['charset']) ? $dsn['charset'] : null,
+ isset($dsn['buffers']) ? $dsn['buffers'] : null,
+ isset($dsn['dialect']) ? $dsn['dialect'] : null,
+ isset($dsn['role']) ? $dsn['role'] : null,
+ );
+
+ $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
+
+ $this->connection = @call_user_func_array($connect_function, $params);
+ if (!$this->connection) {
+ return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @ibase_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @ibase_query($this->connection, $query);
+
+ if (!$result) {
+ return $this->ibaseRaiseError();
+ }
+ if ($this->autocommit && $ismanip) {
+ @ibase_commit($this->connection);
+ }
+ if ($ismanip) {
+ $this->affected = $result;
+ return DB_OK;
+ } else {
+ $this->affected = 0;
+ return $result;
+ }
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * Only works with Firebird.
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if ($this->dsn['dbsyntax'] == 'firebird') {
+ $query = preg_replace('/^([\s(])*SELECT/i',
+ "SELECT FIRST $count SKIP $from", $query);
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal ibase result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ if (function_exists('ibase_fetch_assoc')) {
+ $arr = @ibase_fetch_assoc($result);
+ } else {
+ $arr = get_object_vars(ibase_fetch_object($result));
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @ibase_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? ibase_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ freeQuery()
+
+ function freeQuery($query)
+ {
+ return is_resource($query) ? ibase_free_query($query) : false;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (is_integer($this->affected)) {
+ return $this->affected;
+ }
+ return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @ibase_num_fields($result);
+ if (!$cols) {
+ return $this->ibaseRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute().
+ *
+ * prepare() requires a generic query as string like <code>
+ * INSERT INTO numbers VALUES (?, ?, ?)
+ * </code>. The <kbd>?</kbd> characters are placeholders.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders. Example: <code>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </code>
+ *
+ * @param string $query query to be prepared
+ * @return mixed DB statement resource on success. DB_Error on failure.
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $token = 0;
+ $types = array();
+ $newquery = '';
+
+ foreach ($tokens as $key => $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ break;
+ default:
+ $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ $newquery .= $tokens[$key] . '?';
+ }
+ }
+
+ $newquery = substr($newquery, 0, -1);
+ $this->last_query = $query;
+ $newquery = $this->modifyQuery($newquery);
+ $stmt = @ibase_prepare($this->connection, $newquery);
+
+ if ($stmt === false) {
+ $stmt = $this->ibaseRaiseError();
+ } else {
+ $this->prepare_types[(int)$stmt] = $types;
+ $this->manip_query[(int)$stmt] = DB::isManip($query);
+ }
+
+ return $stmt;
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare().
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 for non-array items or the
+ * quantity of elements in the array.
+ * @return object a new DB_Result or a DB_Error when fail
+ * @see DB_ibase::prepare()
+ * @access public
+ */
+ function &execute($stmt, $data = array())
+ {
+ $data = (array)$data;
+ $this->last_parameters = $data;
+
+ $types = $this->prepare_types[(int)$stmt];
+ if (count($types) != count($data)) {
+ $tmp = $this->raiseError(DB_ERROR_MISMATCH);
+ return $tmp;
+ }
+
+ $i = 0;
+ foreach ($data as $key => $value) {
+ if ($types[$i] == DB_PARAM_MISC) {
+ /*
+ * ibase doesn't seem to have the ability to pass a
+ * parameter along unchanged, so strip off quotes from start
+ * and end, plus turn two single quotes to one single quote,
+ * in order to avoid the quotes getting escaped by
+ * ibase and ending up in the database.
+ */
+ $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
+ $data[$key] = str_replace("''", "'", $data[$key]);
+ } elseif ($types[$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($data[$key], 'rb');
+ if (!$fp) {
+ $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ return $tmp;
+ }
+ $data[$key] = fread($fp, filesize($data[$key]));
+ fclose($fp);
+ }
+ $i++;
+ }
+
+ array_unshift($data, $stmt);
+
+ $res = call_user_func_array('ibase_execute', $data);
+ if (!$res) {
+ $tmp = $this->ibaseRaiseError();
+ return $tmp;
+ }
+ /* XXX need this?
+ if ($this->autocommit && $this->manip_query[(int)$stmt]) {
+ @ibase_commit($this->connection);
+ }*/
+ $this->last_stmt = $stmt;
+ if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
+ $this->_last_query_manip = true;
+ $this->_next_query_manip = false;
+ $tmp = DB_OK;
+ } else {
+ $this->_last_query_manip = false;
+ $tmp = new DB_result($this, $res);
+ }
+ return $tmp;
+ }
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's PHP resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_ibase::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ if (!is_resource($stmt)) {
+ return false;
+ }
+ if ($free_resource) {
+ @ibase_free_query($stmt);
+ }
+ unset($this->prepare_tokens[(int)$stmt]);
+ unset($this->prepare_types[(int)$stmt]);
+ unset($this->manip_query[(int)$stmt]);
+ return true;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ $this->autocommit = $onoff ? 1 : 0;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ return @ibase_commit($this->connection);
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ return @ibase_rollback($this->connection);
+ }
+
+ // }}}
+ // {{{ transactionInit()
+
+ function transactionInit($trans_args = 0)
+ {
+ return $trans_args
+ ? @ibase_trans($trans_args, $this->connection)
+ : @ibase_trans();
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_ibase::createSequence(), DB_ibase::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $sqn = strtoupper($this->getSequenceName($seq_name));
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
+ . 'FROM RDB$GENERATORS '
+ . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result)) {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $result;
+ }
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_ibase::nextID(), DB_ibase::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $sqn = strtoupper($this->getSequenceName($seq_name));
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("CREATE GENERATOR ${sqn}");
+ $this->popErrorHandling();
+
+ return $result;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_ibase::nextID(), DB_ibase::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DELETE FROM RDB$GENERATORS '
+ . "WHERE RDB\$GENERATOR_NAME='"
+ . strtoupper($this->getSequenceName($seq_name))
+ . "'");
+ }
+
+ // }}}
+ // {{{ _ibaseFieldFlags()
+
+ /**
+ * Get the column's flags
+ *
+ * Supports "primary_key", "unique_key", "not_null", "default",
+ * "computed" and "blob".
+ *
+ * @param string $field_name the name of the field
+ * @param string $table_name the name of the table
+ *
+ * @return string the flags
+ *
+ * @access private
+ */
+ function _ibaseFieldFlags($field_name, $table_name)
+ {
+ $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
+ .' FROM RDB$INDEX_SEGMENTS I'
+ .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
+ .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
+ .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
+
+ $result = @ibase_query($this->connection, $sql);
+ if (!$result) {
+ return $this->ibaseRaiseError();
+ }
+
+ $flags = '';
+ if ($obj = @ibase_fetch_object($result)) {
+ @ibase_free_result($result);
+ if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') {
+ $flags .= 'primary_key ';
+ }
+ if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') {
+ $flags .= 'unique_key ';
+ }
+ }
+
+ $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
+ .' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
+ .' F.RDB$FIELD_TYPE AS FTYPE,'
+ .' F.RDB$COMPUTED_SOURCE AS CSOURCE'
+ .' FROM RDB$RELATION_FIELDS R '
+ .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
+ .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
+ .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
+
+ $result = @ibase_query($this->connection, $sql);
+ if (!$result) {
+ return $this->ibaseRaiseError();
+ }
+ if ($obj = @ibase_fetch_object($result)) {
+ @ibase_free_result($result);
+ if (isset($obj->NFLAG)) {
+ $flags .= 'not_null ';
+ }
+ if (isset($obj->DSOURCE)) {
+ $flags .= 'default ';
+ }
+ if (isset($obj->CSOURCE)) {
+ $flags .= 'computed ';
+ }
+ if (isset($obj->FTYPE) && $obj->FTYPE == 261) {
+ $flags .= 'blob ';
+ }
+ }
+
+ return trim($flags);
+ }
+
+ // }}}
+ // {{{ ibaseRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_ibase::errorNative(), DB_ibase::errorCode()
+ */
+ function &ibaseRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $errno = $this->errorCode($this->errorNative());
+ }
+ $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code. NULL if there is no error code.
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function errorNative()
+ {
+ if (function_exists('ibase_errcode')) {
+ return @ibase_errcode();
+ }
+ if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
+ @ibase_errmsg(), $m)) {
+ return (int)$m[1];
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones
+ *
+ * @param int $nativecode the error code returned by the DBMS
+ *
+ * @return int the portable DB error code. Return DB_ERROR if the
+ * current driver doesn't have a mapping for the
+ * $nativecode submitted.
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function errorCode($nativecode = null)
+ {
+ if (isset($this->errorcode_map[$nativecode])) {
+ return $this->errorcode_map[$nativecode];
+ }
+
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/generator .* is not defined/'
+ => DB_ERROR_SYNTAX, // for compat. w ibase_errcode()
+ '/table.*(not exist|not found|unknown)/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/table .* already exists/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/unsuccessful metadata update .* not found/i'
+ => DB_ERROR_NOT_FOUND,
+ '/validation error for column .* value "\*\*\* null/i'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/violation of [\w ]+ constraint/i'
+ => DB_ERROR_CONSTRAINT,
+ '/conversion error from string/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/no permission for/i'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/arithmetic exception, numeric overflow, or string truncation/i'
+ => DB_ERROR_INVALID,
+ '/feature is not supported/i'
+ => DB_ERROR_NOT_CAPABLE,
+ );
+ }
+
+ $errormsg = @ibase_errmsg();
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @ibase_query($this->connection,
+ "SELECT * FROM $result WHERE 1=0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @ibase_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $info = @ibase_field_info($id, $i);
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func($info['name']),
+ 'type' => $info['type'],
+ 'len' => $info['length'],
+ 'flags' => ($got_string)
+ ? $this->_ibaseFieldFlags($info['name'], $result)
+ : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @ibase_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
+ . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
+ case 'views':
+ return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
+ case 'users':
+ return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/ifx.php b/_darcs/pristine/extlib/DB/ifx.php
new file mode 100644
index 000000000..baa6f2867
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/ifx.php
@@ -0,0 +1,683 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's ifx extension
+ * for interacting with Informix databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: ifx.php,v 1.75 2007/07/06 05:19:21 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's ifx extension
+ * for interacting with Informix databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * More info on Informix errors can be found at:
+ * http://www.informix.com/answers/english/ierrors.htm
+ *
+ * TODO:
+ * - set needed env Informix vars on connect
+ * - implement native prepare/execute
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_ifx extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'ifx';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'ifx';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => 'emulate',
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ '-201' => DB_ERROR_SYNTAX,
+ '-206' => DB_ERROR_NOSUCHTABLE,
+ '-217' => DB_ERROR_NOSUCHFIELD,
+ '-236' => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '-239' => DB_ERROR_CONSTRAINT,
+ '-253' => DB_ERROR_SYNTAX,
+ '-268' => DB_ERROR_CONSTRAINT,
+ '-292' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-310' => DB_ERROR_ALREADY_EXISTS,
+ '-316' => DB_ERROR_ALREADY_EXISTS,
+ '-319' => DB_ERROR_NOT_FOUND,
+ '-329' => DB_ERROR_NODBSELECTED,
+ '-346' => DB_ERROR_CONSTRAINT,
+ '-386' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-391' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-554' => DB_ERROR_SYNTAX,
+ '-691' => DB_ERROR_CONSTRAINT,
+ '-692' => DB_ERROR_CONSTRAINT,
+ '-703' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-1202' => DB_ERROR_DIVZERO,
+ '-1204' => DB_ERROR_INVALID_DATE,
+ '-1205' => DB_ERROR_INVALID_DATE,
+ '-1206' => DB_ERROR_INVALID_DATE,
+ '-1209' => DB_ERROR_INVALID_DATE,
+ '-1210' => DB_ERROR_INVALID_DATE,
+ '-1212' => DB_ERROR_INVALID_DATE,
+ '-1213' => DB_ERROR_INVALID_NUMBER,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ * @access private
+ */
+ var $affected = 0;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_ifx()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('informix') &&
+ !PEAR::loadExtension('Informix'))
+ {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
+ $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
+ $user = $dsn['username'] ? $dsn['username'] : '';
+ $pw = $dsn['password'] ? $dsn['password'] : '';
+
+ $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
+
+ $this->connection = @$connect_function($dbname, $user, $pw);
+ if (!is_resource($this->connection)) {
+ return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @ifx_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ $this->affected = null;
+ if (preg_match('/(SELECT|EXECUTE)/i', $query)) { //TESTME: Use !DB::isManip()?
+ // the scroll is needed for fetching absolute row numbers
+ // in a select query result
+ $result = @ifx_query($query, $this->connection, IFX_SCROLL);
+ } else {
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @ifx_query('BEGIN WORK', $this->connection);
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @ifx_query($query, $this->connection);
+ }
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ $this->affected = @ifx_affected_rows($result);
+ // Determine which queries should return data, and which
+ // should return an error code only.
+ if (preg_match('/(SELECT|EXECUTE)/i', $query)) {
+ return $result;
+ }
+ // XXX Testme: free results inside a transaction
+ // may cause to stop it and commit the work?
+
+ // Result has to be freed even with a insert or update
+ @ifx_free_result($result);
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal ifx result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->_last_query_manip) {
+ return $this->affected;
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if (($rownum !== null) && ($rownum < 0)) {
+ return null;
+ }
+ if ($rownum === null) {
+ /*
+ * Even though fetch_row() should return the next row if
+ * $rownum is null, it doesn't in all cases. Bug 598.
+ */
+ $rownum = 'NEXT';
+ } else {
+ // Index starts at row 1, unlike most DBMS's starting at 0.
+ $rownum++;
+ }
+ if (!$arr = @ifx_fetch_row($result, $rownum)) {
+ return null;
+ }
+ if ($fetchmode !== DB_FETCHMODE_ASSOC) {
+ $i=0;
+ $order = array();
+ foreach ($arr as $val) {
+ $order[$i++] = $val;
+ }
+ $arr = $order;
+ } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
+ $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
+ {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ if (!$cols = @ifx_num_fields($result)) {
+ return $this->ifxRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? ifx_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = true)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @ifx_query('COMMIT WORK', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @ifx_query('ROLLBACK WORK', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ ifxRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_ifx::errorNative(), DB_ifx::errorCode()
+ */
+ function ifxRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $errno = $this->errorCode(ifx_error());
+ }
+ return $this->raiseError($errno, null, null, null,
+ $this->errorNative());
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code and message produced by the last query
+ *
+ * @return string the DBMS' error code and message
+ */
+ function errorNative()
+ {
+ return @ifx_error() . ' ' . @ifx_errormsg();
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones.
+ *
+ * Requires that the DB implementation's constructor fills
+ * in the <var>$errorcode_map</var> property.
+ *
+ * @param string $nativecode error code returned by the database
+ * @return int a portable DB error code, or DB_ERROR if this DB
+ * implementation has no mapping for the given error code.
+ */
+ function errorCode($nativecode)
+ {
+ if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
+ $code = $match[1];
+ if (isset($this->errorcode_map[$code])) {
+ return $this->errorcode_map[$code];
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' if <var>$result</var> is a table name.
+ *
+ * If analyzing a query result and the result has duplicate field names,
+ * an error will be raised saying
+ * <samp>can't distinguish duplicate field names</samp>.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.6.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ $flds = @ifx_fieldproperties($id);
+ $count = @ifx_num_fields($id);
+
+ if (count($flds) != $count) {
+ return $this->raiseError("can't distinguish duplicate field names");
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $i = 0;
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ foreach ($flds as $key => $value) {
+ $props = explode(';', $value);
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func($key),
+ 'type' => $props[0],
+ 'len' => $props[1],
+ 'flags' => $props[4] == 'N' ? 'not_null' : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ $i++;
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @ifx_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT tabname FROM systables WHERE tabid >= 100';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/msql.php b/_darcs/pristine/extlib/DB/msql.php
new file mode 100644
index 000000000..34854f472
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/msql.php
@@ -0,0 +1,831 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's msql extension
+ * for interacting with Mini SQL databases
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: msql.php,v 1.64 2007/09/21 13:40:41 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's msql extension
+ * for interacting with Mini SQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ * @since Class not functional until Release 1.7.0
+ */
+class DB_msql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'msql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'msql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * The query result resource created by PHP
+ *
+ * Used to make affectedRows() work. Only contains the result for
+ * data manipulation queries. Contains false for other queries.
+ *
+ * @var resource
+ * @access private
+ */
+ var $_result;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_msql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * Example of how to connect:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * // $dsn = 'msql://hostname/dbname'; // use a TCP connection
+ * $dsn = 'msql:///dbname'; // use a socket
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db = DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('msql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array();
+ if ($dsn['hostspec']) {
+ $params[] = $dsn['port']
+ ? $dsn['hostspec'] . ',' . $dsn['port']
+ : $dsn['hostspec'];
+ }
+
+ $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ @ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ @ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ if (($err = @msql_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if (!@msql_select_db($dsn['database'], $this->connection)) {
+ return $this->msqlRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @msql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @msql_query($query, $this->connection);
+ if (!$result) {
+ return $this->msqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if ($this->_checkManip($query)) {
+ $this->_result = $result;
+ return DB_OK;
+ } else {
+ $this->_result = false;
+ return $result;
+ }
+ }
+
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal msql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@msql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @msql_fetch_array($result, MSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @msql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? msql_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @msql_num_fields($result);
+ if (!$cols) {
+ return $this->msqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @msql_num_rows($result);
+ if ($rows === false) {
+ return $this->msqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affected()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (!$this->_result) {
+ return 0;
+ }
+ return msql_affected_rows($this->_result);
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_msql::createSequence(), DB_msql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = false;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("SELECT _seq FROM ${seqname}");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = true;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * Also creates a new table to associate the sequence with. Uses
+ * a separate table to ensure portability with other drivers.
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_msql::nextID(), DB_msql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER NOT NULL)');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ $res = $this->query("CREATE SEQUENCE ON ${seqname}");
+ return $res;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_msql::nextID(), DB_msql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * mSQL does not support delimited identifiers
+ *
+ * @param string $str the identifier name to be quoted
+ *
+ * @return object a DB_Error object
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.7.0
+ */
+ function quoteIdentifier($str)
+ {
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ }
+
+ // }}}
+ // {{{ quoteFloat()
+
+ /**
+ * Formats a float value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param float the float value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteFloat($float) {
+ return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.7.0
+ */
+ function escapeSimple($str)
+ {
+ return addslashes($str);
+ }
+
+ // }}}
+ // {{{ msqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_msql::errorNative(), DB_msql::errorCode()
+ */
+ function msqlRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @msql_error();
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message
+ *
+ * @param string $errormsg the error message returned from the database
+ *
+ * @return integer the error number from a DB_ERROR* constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+
+ // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+ // this hack to work around it, per bug #9599.
+ $errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg);
+
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/^Access to database denied/i'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^Bad index name/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/^Bad order field/i'
+ => DB_ERROR_SYNTAX,
+ '/^Bad type for comparison/i'
+ => DB_ERROR_SYNTAX,
+ '/^Can\'t perform LIKE on/i'
+ => DB_ERROR_SYNTAX,
+ '/^Can\'t use TEXT fields in LIKE comparison/i'
+ => DB_ERROR_SYNTAX,
+ '/^Couldn\'t create temporary table/i'
+ => DB_ERROR_CANNOT_CREATE,
+ '/^Error creating table file/i'
+ => DB_ERROR_CANNOT_CREATE,
+ '/^Field .* cannot be null$/i'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/^Index (field|condition) .* cannot be null$/i'
+ => DB_ERROR_SYNTAX,
+ '/^Invalid date format/i'
+ => DB_ERROR_INVALID_DATE,
+ '/^Invalid time format/i'
+ => DB_ERROR_INVALID,
+ '/^Literal value for .* is wrong type$/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/^No Database Selected/i'
+ => DB_ERROR_NODBSELECTED,
+ '/^No value specified for field/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '/^Non unique value for unique index/i'
+ => DB_ERROR_CONSTRAINT,
+ '/^Out of memory for temporary table/i'
+ => DB_ERROR_CANNOT_CREATE,
+ '/^Permission denied/i'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^Reference to un-selected table/i'
+ => DB_ERROR_SYNTAX,
+ '/^syntax error/i'
+ => DB_ERROR_SYNTAX,
+ '/^Table .* exists$/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/^Unknown database/i'
+ => DB_ERROR_NOSUCHDB,
+ '/^Unknown field/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/^Unknown (index|system variable)/i'
+ => DB_ERROR_NOT_FOUND,
+ '/^Unknown table/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/^Unqualified field/i'
+ => DB_ERROR_SYNTAX,
+ );
+ }
+
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @msql_query("SELECT * FROM $result",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @msql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $tmp = @msql_fetch_field($id);
+
+ $flags = '';
+ if ($tmp->not_null) {
+ $flags .= 'not_null ';
+ }
+ if ($tmp->unique) {
+ $flags .= 'unique_key ';
+ }
+ $flags = trim($flags);
+
+ $res[$i] = array(
+ 'table' => $case_func($tmp->table),
+ 'name' => $case_func($tmp->name),
+ 'type' => $tmp->type,
+ 'len' => msql_field_len($id, $i),
+ 'flags' => $flags,
+ );
+
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @msql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtain a list of a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return array the array containing the list of objects requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'databases':
+ $id = @msql_list_dbs($this->connection);
+ break;
+ case 'tables':
+ $id = @msql_list_tables($this->dsn['database'],
+ $this->connection);
+ break;
+ default:
+ return null;
+ }
+ if (!$id) {
+ return $this->msqlRaiseError();
+ }
+ $out = array();
+ while ($row = @msql_fetch_row($id)) {
+ $out[] = $row[0];
+ }
+ return $out;
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/mssql.php b/_darcs/pristine/extlib/DB/mssql.php
new file mode 100644
index 000000000..511a2b686
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/mssql.php
@@ -0,0 +1,963 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mssql extension
+ * for interacting with Microsoft SQL Server databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mssql.php,v 1.92 2007/09/21 13:40:41 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mssql extension
+ * for interacting with Microsoft SQL Server databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * DB's mssql driver is only for Microsfoft SQL Server databases.
+ *
+ * If you're connecting to a Sybase database, you MUST specify "sybase"
+ * as the "phptype" in the DSN.
+ *
+ * This class only works correctly if you have compiled PHP using
+ * --with-mssql=[dir_to_FreeTDS].
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_mssql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mssql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mssql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
+ var $errorcode_map = array(
+ 102 => DB_ERROR_SYNTAX,
+ 110 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 155 => DB_ERROR_NOSUCHFIELD,
+ 156 => DB_ERROR_SYNTAX,
+ 170 => DB_ERROR_SYNTAX,
+ 207 => DB_ERROR_NOSUCHFIELD,
+ 208 => DB_ERROR_NOSUCHTABLE,
+ 245 => DB_ERROR_INVALID_NUMBER,
+ 319 => DB_ERROR_SYNTAX,
+ 321 => DB_ERROR_NOSUCHFIELD,
+ 325 => DB_ERROR_SYNTAX,
+ 336 => DB_ERROR_SYNTAX,
+ 515 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 547 => DB_ERROR_CONSTRAINT,
+ 1018 => DB_ERROR_SYNTAX,
+ 1035 => DB_ERROR_SYNTAX,
+ 1913 => DB_ERROR_ALREADY_EXISTS,
+ 2209 => DB_ERROR_SYNTAX,
+ 2223 => DB_ERROR_SYNTAX,
+ 2248 => DB_ERROR_SYNTAX,
+ 2256 => DB_ERROR_SYNTAX,
+ 2257 => DB_ERROR_SYNTAX,
+ 2627 => DB_ERROR_CONSTRAINT,
+ 2714 => DB_ERROR_ALREADY_EXISTS,
+ 3607 => DB_ERROR_DIVZERO,
+ 3701 => DB_ERROR_NOSUCHTABLE,
+ 7630 => DB_ERROR_SYNTAX,
+ 8134 => DB_ERROR_DIVZERO,
+ 9303 => DB_ERROR_SYNTAX,
+ 9317 => DB_ERROR_SYNTAX,
+ 9318 => DB_ERROR_SYNTAX,
+ 9331 => DB_ERROR_SYNTAX,
+ 9332 => DB_ERROR_SYNTAX,
+ 15253 => DB_ERROR_SYNTAX,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = null;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mssql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase')
+ && !PEAR::loadExtension('sybase_ct'))
+ {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array(
+ $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
+ $dsn['username'] ? $dsn['username'] : null,
+ $dsn['password'] ? $dsn['password'] : null,
+ );
+ if ($dsn['port']) {
+ $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
+ . $dsn['port'];
+ }
+
+ $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
+
+ $this->connection = @call_user_func_array($connect_function, $params);
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ @mssql_get_last_message());
+ }
+ if ($dsn['database']) {
+ if (!@mssql_select_db($dsn['database'], $this->connection)) {
+ return $this->raiseError(DB_ERROR_NODBSELECTED,
+ null, null, null,
+ @mssql_get_last_message());
+ }
+ $this->_db = $dsn['database'];
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mssql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mssql_query('BEGIN TRAN', $this->connection);
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @mssql_query($query, $this->connection);
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ return $ismanip ? DB_OK : $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mssql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return @mssql_next_result($result);
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mssql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mssql_fetch_assoc($result);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mssql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? mssql_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mssql_num_fields($result);
+ if (!$cols) {
+ return $this->mssqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mssql_num_rows($result);
+ if ($rows === false) {
+ return $this->mssqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @mssql_query('COMMIT TRAN', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @mssql_query('ROLLBACK TRAN', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->_last_query_manip) {
+ $res = @mssql_query('select @@rowcount', $this->connection);
+ if (!$res) {
+ return $this->mssqlRaiseError();
+ }
+ $ar = @mssql_fetch_row($res);
+ if (!$ar) {
+ $result = 0;
+ } else {
+ @mssql_free_result($res);
+ $result = $ar[0];
+ }
+ } else {
+ $result = 0;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mssql::createSequence(), DB_mssql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
+ {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } elseif (!DB::isError($result)) {
+ $result = $this->query("SELECT IDENT_CURRENT('$seqname')");
+ if (DB::isError($result)) {
+ /* Fallback code for MS SQL Server 7.0, which doesn't have
+ * IDENT_CURRENT. This is *not* safe for concurrent
+ * requests, and really, if you're using it, you're in a
+ * world of hurt. Nevertheless, it's here to ensure BC. See
+ * bug #181 for the gory details.*/
+ $result = $this->query("SELECT @@IDENTITY FROM $seqname");
+ }
+ $repeat = 0;
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ return $result[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mssql::nextID(), DB_mssql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE TABLE '
+ . $this->getSequenceName($seq_name)
+ . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
+ . ' [vapor] [int] NULL)');
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mssql::nextID(), DB_mssql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '[' . str_replace(']', ']]', $str) . ']';
+ }
+
+ // }}}
+ // {{{ mssqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mssql::errorNative(), DB_mssql::errorCode()
+ */
+ function mssqlRaiseError($code = null)
+ {
+ $message = @mssql_get_last_message();
+ if (!$code) {
+ $code = $this->errorNative();
+ }
+ return $this->raiseError($this->errorCode($code, $message),
+ null, null, null, "$code - $message");
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
+ if (!$res) {
+ return DB_ERROR;
+ }
+ $row = @mssql_fetch_row($res);
+ return $row[0];
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from mssql's native codes.
+ *
+ * If <var>$nativecode</var> isn't known yet, it will be looked up.
+ *
+ * @param mixed $nativecode mssql error code, if known
+ * @return integer an error number from a DB error constant
+ * @see errorNative()
+ */
+ function errorCode($nativecode = null, $msg = '')
+ {
+ if (!$nativecode) {
+ $nativecode = $this->errorNative();
+ }
+ if (isset($this->errorcode_map[$nativecode])) {
+ if ($nativecode == 3701
+ && preg_match('/Cannot drop the index/i', $msg))
+ {
+ return DB_ERROR_NOT_FOUND;
+ }
+ return $this->errorcode_map[$nativecode];
+ } else {
+ return DB_ERROR;
+ }
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $id = @mssql_query("SELECT * FROM $result WHERE 1=0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mssql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ if ($got_string) {
+ $flags = $this->_mssql_field_flags($result,
+ @mssql_field_name($id, $i));
+ if (DB::isError($flags)) {
+ return $flags;
+ }
+ } else {
+ $flags = '';
+ }
+
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@mssql_field_name($id, $i)),
+ 'type' => @mssql_field_type($id, $i),
+ 'len' => @mssql_field_length($id, $i),
+ 'flags' => $flags,
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mssql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _mssql_field_flags()
+
+ /**
+ * Get a column's flags
+ *
+ * Supports "not_null", "primary_key",
+ * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
+ * "unique_key" (mssql unique index, unique check or primary_key) and
+ * "multiple_key" (multikey index)
+ *
+ * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
+ * not useful at all - is the behaviour of mysql_field_flags that primary
+ * keys are alway unique? is the interpretation of multiple_key correct?
+ *
+ * @param string $table the table name
+ * @param string $column the field name
+ *
+ * @return string the flags
+ *
+ * @access private
+ * @author Joern Barthel <j_barthel@web.de>
+ */
+ function _mssql_field_flags($table, $column)
+ {
+ static $tableName = null;
+ static $flags = array();
+
+ if ($table != $tableName) {
+
+ $flags = array();
+ $tableName = $table;
+
+ // get unique and primary keys
+ $res = $this->getAll("EXEC SP_HELPINDEX $table", DB_FETCHMODE_ASSOC);
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ foreach ($res as $val) {
+ $keys = explode(', ', $val['index_keys']);
+
+ if (sizeof($keys) > 1) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'multiple_key');
+ }
+ }
+
+ if (strpos($val['index_description'], 'primary key')) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'primary_key');
+ }
+ } elseif (strpos($val['index_description'], 'unique')) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'unique_key');
+ }
+ }
+ }
+
+ // get auto_increment, not_null and timestamp
+ $res = $this->getAll("EXEC SP_COLUMNS $table", DB_FETCHMODE_ASSOC);
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ foreach ($res as $val) {
+ $val = array_change_key_case($val, CASE_LOWER);
+ if ($val['nullable'] == '0') {
+ $this->_add_flag($flags[$val['column_name']], 'not_null');
+ }
+ if (strpos($val['type_name'], 'identity')) {
+ $this->_add_flag($flags[$val['column_name']], 'auto_increment');
+ }
+ if (strpos($val['type_name'], 'timestamp')) {
+ $this->_add_flag($flags[$val['column_name']], 'timestamp');
+ }
+ }
+ }
+
+ if (array_key_exists($column, $flags)) {
+ return(implode(' ', $flags[$column]));
+ }
+ return '';
+ }
+
+ // }}}
+ // {{{ _add_flag()
+
+ /**
+ * Adds a string to the flags array if the flag is not yet in there
+ * - if there is no flag present the array is created
+ *
+ * @param array &$array the reference to the flag-array
+ * @param string $value the flag value
+ *
+ * @return void
+ *
+ * @access private
+ * @author Joern Barthel <j_barthel@web.de>
+ */
+ function _add_flag(&$array, $value)
+ {
+ if (!is_array($array)) {
+ $array = array($value);
+ } elseif (!in_array($value, $array)) {
+ array_push($array, $value);
+ }
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return "SELECT name FROM sysobjects WHERE type = 'U'"
+ . ' ORDER BY name';
+ case 'views':
+ return "SELECT name FROM sysobjects WHERE type = 'V'";
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/mysql.php b/_darcs/pristine/extlib/DB/mysql.php
new file mode 100644
index 000000000..c67254520
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/mysql.php
@@ -0,0 +1,1045 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mysql extension
+ * for interacting with MySQL databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mysql.php,v 1.126 2007/09/21 13:32:52 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mysql extension
+ * for interacting with MySQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_mysql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mysql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mysql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '4.2.0',
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1004 => DB_ERROR_CANNOT_CREATE,
+ 1005 => DB_ERROR_CANNOT_CREATE,
+ 1006 => DB_ERROR_CANNOT_CREATE,
+ 1007 => DB_ERROR_ALREADY_EXISTS,
+ 1008 => DB_ERROR_CANNOT_DROP,
+ 1022 => DB_ERROR_ALREADY_EXISTS,
+ 1044 => DB_ERROR_ACCESS_VIOLATION,
+ 1046 => DB_ERROR_NODBSELECTED,
+ 1048 => DB_ERROR_CONSTRAINT,
+ 1049 => DB_ERROR_NOSUCHDB,
+ 1050 => DB_ERROR_ALREADY_EXISTS,
+ 1051 => DB_ERROR_NOSUCHTABLE,
+ 1054 => DB_ERROR_NOSUCHFIELD,
+ 1061 => DB_ERROR_ALREADY_EXISTS,
+ 1062 => DB_ERROR_ALREADY_EXISTS,
+ 1064 => DB_ERROR_SYNTAX,
+ 1091 => DB_ERROR_NOT_FOUND,
+ 1100 => DB_ERROR_NOT_LOCKED,
+ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 1142 => DB_ERROR_ACCESS_VIOLATION,
+ 1146 => DB_ERROR_NOSUCHTABLE,
+ 1216 => DB_ERROR_CONSTRAINT,
+ 1217 => DB_ERROR_CONSTRAINT,
+ 1356 => DB_ERROR_DIVZERO,
+ 1451 => DB_ERROR_CONSTRAINT,
+ 1452 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mysql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's mysql driver supports the following extra DSN options:
+ * + new_link If set to true, causes subsequent calls to connect()
+ * to return a new connection link instead of the
+ * existing one. WARNING: this is not portable to
+ * other DBMS's. Available since PEAR DB 1.7.0.
+ * + client_flags Any combination of MYSQL_CLIENT_* constants.
+ * Only used if PHP is at version 4.3.0 or greater.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mysql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array();
+ if ($dsn['protocol'] && $dsn['protocol'] == 'unix') {
+ $params[0] = ':' . $dsn['socket'];
+ } else {
+ $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
+ : 'localhost';
+ if ($dsn['port']) {
+ $params[0] .= ':' . $dsn['port'];
+ }
+ }
+ $params[] = $dsn['username'] ? $dsn['username'] : null;
+ $params[] = $dsn['password'] ? $dsn['password'] : null;
+
+ if (!$persistent) {
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ $params[] = true;
+ } else {
+ $params[] = false;
+ }
+ }
+ if (version_compare(phpversion(), '4.3.0', '>=')) {
+ $params[] = isset($dsn['client_flags'])
+ ? $dsn['client_flags'] : null;
+ }
+
+ $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ @ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ @ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ if (($err = @mysql_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if ($dsn['database']) {
+ if (!@mysql_select_db($dsn['database'], $this->connection)) {
+ return $this->mysqlRaiseError();
+ }
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mysql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * Generally uses mysql_query(). If you want to use
+ * mysql_unbuffered_query() set the "result_buffering" option to 0 using
+ * setOptions(). This option was added in Release 1.7.0.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
+ $result = @mysql_query('BEGIN', $this->connection);
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ if (!$this->options['result_buffering']) {
+ $result = @mysql_unbuffered_query($query, $this->connection);
+ } else {
+ $result = @mysql_query($query, $this->connection);
+ }
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ if (is_resource($result)) {
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mysql result pointer to the next available result
+ *
+ * This method has not been implemented yet.
+ *
+ * @param a valid sql result resource
+ *
+ * @return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mysql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mysql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? mysql_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mysql_num_fields($result);
+ if (!$cols) {
+ return $this->mysqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mysql_num_rows($result);
+ if ($rows === null) {
+ return $this->mysqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysql_query('COMMIT', $this->connection);
+ $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysql_query('ROLLBACK', $this->connection);
+ $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->_last_query_manip) {
+ return @mysql_affected_rows($this->connection);
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mysql::createSequence(), DB_mysql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("UPDATE ${seqname} ".
+ 'SET id=LAST_INSERT_ID(id+1)');
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ // COMMON CASE
+ $id = @mysql_insert_id($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+ // EMPTY SEQ TABLE
+ // Sequence table must be empty for some reason, so fill
+ // it and return 1 and obtain a user-level lock
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ if ($result == 0) {
+ // Failed to get the lock
+ return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ // add the default value
+ $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ // Release the lock
+ $result = $this->getOne('SELECT RELEASE_LOCK('
+ . "'${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ // We know what the result will be, so no need to try again
+ return 1;
+
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ // ONDEMAND TABLE CREATION
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ $repeat = 1;
+ }
+
+ } elseif (DB::isError($result) &&
+ $result->getCode() == DB_ERROR_ALREADY_EXISTS)
+ {
+ // BACKWARDS COMPAT
+ // see _BCsequence() comment
+ $result = $this->_BCsequence($seqname);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $repeat = 1;
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // insert yields value 1, nextId call will generate ID 2
+ $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // so reset to zero
+ return $this->query("UPDATE ${seqname} SET id = 0");
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ _BCsequence()
+
+ /**
+ * Backwards compatibility with old sequence emulation implementation
+ * (clean up the dupes)
+ *
+ * @param string $seqname the sequence name to clean up
+ *
+ * @return bool true on success. A DB_Error object on failure.
+ *
+ * @access private
+ */
+ function _BCsequence($seqname)
+ {
+ // Obtain a user-level lock... this will release any previous
+ // application locks, but unlike LOCK TABLES, it does not abort
+ // the current transaction and is much less frequently used.
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ if ($result == 0) {
+ // Failed to get the lock, can't do the conversion, bail
+ // with a DB_ERROR_NOT_LOCKED error
+ return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
+ if (DB::isError($highest_id)) {
+ return $highest_id;
+ }
+ // This should kill all rows except the highest
+ // We should probably do something if $highest_id isn't
+ // numeric, but I'm at a loss as how to handle that...
+ $result = $this->query('DELETE FROM ' . $seqname
+ . " WHERE id <> $highest_id");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ // If another thread has been waiting for this lock,
+ // it will go thru the above procedure, but will have no
+ // real effect
+ $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ * (WARNING: using names that require this is a REALLY BAD IDEA)
+ *
+ * WARNING: Older versions of MySQL can't handle the backtick
+ * character (<kbd>`</kbd>) in table or column names.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '`' . str_replace('`', '``', $str) . '`';
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ if (function_exists('mysql_real_escape_string')) {
+ return @mysql_real_escape_string($str, $this->connection);
+ } else {
+ return @mysql_escape_string($str);
+ }
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * This little hack lets you know how many rows were deleted
+ * when running a "DELETE FROM table" query. Only implemented
+ * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_common::setOption()
+ */
+ function modifyQuery($query)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
+ // "DELETE FROM table" gives 0 affected rows in MySQL.
+ // This little hack lets you know how many rows were deleted.
+ if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+ $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+ 'DELETE FROM \1 WHERE 1=1', $query);
+ }
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query) || $this->_next_query_manip) {
+ return $query . " LIMIT $count";
+ } else {
+ return $query . " LIMIT $from, $count";
+ }
+ }
+
+ // }}}
+ // {{{ mysqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mysql::errorNative(), DB_common::errorCode()
+ */
+ function mysqlRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
+ $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
+ }
+ $errno = $this->errorCode(mysql_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @mysql_errno($this->connection) . ' ** ' .
+ @mysql_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @mysql_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ // Fix for bug #11580.
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @mysql_query("SELECT * FROM $result LIMIT 0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mysql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $case_func(@mysql_field_table($id, $i)),
+ 'name' => $case_func(@mysql_field_name($id, $i)),
+ 'type' => @mysql_field_type($id, $i),
+ 'len' => @mysql_field_len($id, $i),
+ 'flags' => @mysql_field_flags($id, $i),
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mysql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SHOW TABLES';
+ case 'users':
+ return 'SELECT DISTINCT User FROM mysql.user';
+ case 'databases':
+ return 'SHOW DATABASES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/mysqli.php b/_darcs/pristine/extlib/DB/mysqli.php
new file mode 100644
index 000000000..c6941b170
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/mysqli.php
@@ -0,0 +1,1092 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mysqli extension
+ * for interacting with MySQL databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mysqli.php,v 1.82 2007/09/21 13:40:41 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mysqli extension
+ * for interacting with MySQL databases
+ *
+ * This is for MySQL versions 4.1 and above. Requires PHP 5.
+ *
+ * Note that persistent connections no longer exist.
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ * @since Class functional since Release 1.6.3
+ */
+class DB_mysqli extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mysqli';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mysqli';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => false,
+ 'prepare' => false,
+ 'ssl' => true,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1004 => DB_ERROR_CANNOT_CREATE,
+ 1005 => DB_ERROR_CANNOT_CREATE,
+ 1006 => DB_ERROR_CANNOT_CREATE,
+ 1007 => DB_ERROR_ALREADY_EXISTS,
+ 1008 => DB_ERROR_CANNOT_DROP,
+ 1022 => DB_ERROR_ALREADY_EXISTS,
+ 1044 => DB_ERROR_ACCESS_VIOLATION,
+ 1046 => DB_ERROR_NODBSELECTED,
+ 1048 => DB_ERROR_CONSTRAINT,
+ 1049 => DB_ERROR_NOSUCHDB,
+ 1050 => DB_ERROR_ALREADY_EXISTS,
+ 1051 => DB_ERROR_NOSUCHTABLE,
+ 1054 => DB_ERROR_NOSUCHFIELD,
+ 1061 => DB_ERROR_ALREADY_EXISTS,
+ 1062 => DB_ERROR_ALREADY_EXISTS,
+ 1064 => DB_ERROR_SYNTAX,
+ 1091 => DB_ERROR_NOT_FOUND,
+ 1100 => DB_ERROR_NOT_LOCKED,
+ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 1142 => DB_ERROR_ACCESS_VIOLATION,
+ 1146 => DB_ERROR_NOSUCHTABLE,
+ 1216 => DB_ERROR_CONSTRAINT,
+ 1217 => DB_ERROR_CONSTRAINT,
+ 1356 => DB_ERROR_DIVZERO,
+ 1451 => DB_ERROR_CONSTRAINT,
+ 1452 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+ /**
+ * Array for converting MYSQLI_*_FLAG constants to text values
+ * @var array
+ * @access public
+ * @since Property available since Release 1.6.5
+ */
+ var $mysqli_flags = array(
+ MYSQLI_NOT_NULL_FLAG => 'not_null',
+ MYSQLI_PRI_KEY_FLAG => 'primary_key',
+ MYSQLI_UNIQUE_KEY_FLAG => 'unique_key',
+ MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key',
+ MYSQLI_BLOB_FLAG => 'blob',
+ MYSQLI_UNSIGNED_FLAG => 'unsigned',
+ MYSQLI_ZEROFILL_FLAG => 'zerofill',
+ MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment',
+ MYSQLI_TIMESTAMP_FLAG => 'timestamp',
+ MYSQLI_SET_FLAG => 'set',
+ // MYSQLI_NUM_FLAG => 'numeric', // unnecessary
+ // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie
+ MYSQLI_GROUP_FLAG => 'group_by'
+ );
+
+ /**
+ * Array for converting MYSQLI_TYPE_* constants to text values
+ * @var array
+ * @access public
+ * @since Property available since Release 1.6.5
+ */
+ var $mysqli_types = array(
+ MYSQLI_TYPE_DECIMAL => 'decimal',
+ MYSQLI_TYPE_TINY => 'tinyint',
+ MYSQLI_TYPE_SHORT => 'int',
+ MYSQLI_TYPE_LONG => 'int',
+ MYSQLI_TYPE_FLOAT => 'float',
+ MYSQLI_TYPE_DOUBLE => 'double',
+ // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it
+ MYSQLI_TYPE_TIMESTAMP => 'timestamp',
+ MYSQLI_TYPE_LONGLONG => 'bigint',
+ MYSQLI_TYPE_INT24 => 'mediumint',
+ MYSQLI_TYPE_DATE => 'date',
+ MYSQLI_TYPE_TIME => 'time',
+ MYSQLI_TYPE_DATETIME => 'datetime',
+ MYSQLI_TYPE_YEAR => 'year',
+ MYSQLI_TYPE_NEWDATE => 'date',
+ MYSQLI_TYPE_ENUM => 'enum',
+ MYSQLI_TYPE_SET => 'set',
+ MYSQLI_TYPE_TINY_BLOB => 'tinyblob',
+ MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
+ MYSQLI_TYPE_LONG_BLOB => 'longblob',
+ MYSQLI_TYPE_BLOB => 'blob',
+ MYSQLI_TYPE_VAR_STRING => 'varchar',
+ MYSQLI_TYPE_STRING => 'char',
+ MYSQLI_TYPE_GEOMETRY => 'geometry',
+ /* These constants are conditionally compiled in ext/mysqli, so we'll
+ * define them by number rather than constant. */
+ 16 => 'bit',
+ 246 => 'decimal',
+ );
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mysqli()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's mysqli driver supports the following extra DSN options:
+ * + When the 'ssl' $option passed to DB::connect() is true:
+ * + key The path to the key file.
+ * + cert The path to the certificate file.
+ * + ca The path to the certificate authority file.
+ * + capath The path to a directory that contains trusted SSL
+ * CA certificates in pem format.
+ * + cipher The list of allowable ciphers for SSL encryption.
+ *
+ * Example of how to connect using SSL:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = array(
+ * 'phptype' => 'mysqli',
+ * 'username' => 'someuser',
+ * 'password' => 'apasswd',
+ * 'hostspec' => 'localhost',
+ * 'database' => 'thedb',
+ * 'key' => 'client-key.pem',
+ * 'cert' => 'client-cert.pem',
+ * 'ca' => 'cacert.pem',
+ * 'capath' => '/path/to/ca/dir',
+ * 'cipher' => 'AES',
+ * );
+ *
+ * $options = array(
+ * 'ssl' => true,
+ * );
+ *
+ * $db = DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mysqli')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $ini = ini_get('track_errors');
+ @ini_set('track_errors', 1);
+ $php_errormsg = '';
+
+ if (((int) $this->getOption('ssl')) === 1) {
+ $init = mysqli_init();
+ mysqli_ssl_set(
+ $init,
+ empty($dsn['key']) ? null : $dsn['key'],
+ empty($dsn['cert']) ? null : $dsn['cert'],
+ empty($dsn['ca']) ? null : $dsn['ca'],
+ empty($dsn['capath']) ? null : $dsn['capath'],
+ empty($dsn['cipher']) ? null : $dsn['cipher']
+ );
+ if ($this->connection = @mysqli_real_connect(
+ $init,
+ $dsn['hostspec'],
+ $dsn['username'],
+ $dsn['password'],
+ $dsn['database'],
+ $dsn['port'],
+ $dsn['socket']))
+ {
+ $this->connection = $init;
+ }
+ } else {
+ $this->connection = @mysqli_connect(
+ $dsn['hostspec'],
+ $dsn['username'],
+ $dsn['password'],
+ $dsn['database'],
+ $dsn['port'],
+ $dsn['socket']
+ );
+ }
+
+ @ini_set('track_errors', $ini);
+
+ if (!$this->connection) {
+ if (($err = @mysqli_connect_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if ($dsn['database']) {
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mysqli_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0');
+ $result = @mysqli_query($this->connection, 'BEGIN');
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @mysqli_query($this->connection, $query);
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ if (is_object($result)) {
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mysql result pointer to the next available result.
+ *
+ * This method has not been implemented yet.
+ *
+ * @param resource $result a valid sql result resource
+ * @return false
+ * @access public
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mysqli_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mysqli_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? mysqli_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mysqli_num_fields($result);
+ if (!$cols) {
+ return $this->mysqliRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mysqli_num_rows($result);
+ if ($rows === null) {
+ return $this->mysqliRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysqli_query($this->connection, 'COMMIT');
+ $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysqli_query($this->connection, 'ROLLBACK');
+ $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->_last_query_manip) {
+ return @mysqli_affected_rows($this->connection);
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mysqli::createSequence(), DB_mysqli::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query('UPDATE ' . $seqname
+ . ' SET id = LAST_INSERT_ID(id + 1)');
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ // COMMON CASE
+ $id = @mysqli_insert_id($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+
+ // EMPTY SEQ TABLE
+ // Sequence table must be empty for some reason,
+ // so fill it and return 1
+ // Obtain a user-level lock
+ $result = $this->getOne('SELECT GET_LOCK('
+ . "'${seqname}_lock', 10)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ if ($result == 0) {
+ return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ // add the default value
+ $result = $this->query('REPLACE INTO ' . $seqname
+ . ' (id) VALUES (0)');
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ // Release the lock
+ $result = $this->getOne('SELECT RELEASE_LOCK('
+ . "'${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ // We know what the result will be, so no need to try again
+ return 1;
+
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ // ONDEMAND TABLE CREATION
+ $result = $this->createSequence($seq_name);
+
+ // Since createSequence initializes the ID to be 1,
+ // we do not need to retrieve the ID again (or we will get 2)
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ // First ID of a newly created sequence is 1
+ return 1;
+ }
+
+ } elseif (DB::isError($result) &&
+ $result->getCode() == DB_ERROR_ALREADY_EXISTS)
+ {
+ // BACKWARDS COMPAT
+ // see _BCsequence() comment
+ $result = $this->_BCsequence($seqname);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $repeat = 1;
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mysqli::nextID(), DB_mysqli::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // insert yields value 1, nextId call will generate ID 2
+ return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ _BCsequence()
+
+ /**
+ * Backwards compatibility with old sequence emulation implementation
+ * (clean up the dupes)
+ *
+ * @param string $seqname the sequence name to clean up
+ *
+ * @return bool true on success. A DB_Error object on failure.
+ *
+ * @access private
+ */
+ function _BCsequence($seqname)
+ {
+ // Obtain a user-level lock... this will release any previous
+ // application locks, but unlike LOCK TABLES, it does not abort
+ // the current transaction and is much less frequently used.
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ if ($result == 0) {
+ // Failed to get the lock, can't do the conversion, bail
+ // with a DB_ERROR_NOT_LOCKED error
+ return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
+ if (DB::isError($highest_id)) {
+ return $highest_id;
+ }
+
+ // This should kill all rows except the highest
+ // We should probably do something if $highest_id isn't
+ // numeric, but I'm at a loss as how to handle that...
+ $result = $this->query('DELETE FROM ' . $seqname
+ . " WHERE id <> $highest_id");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ // If another thread has been waiting for this lock,
+ // it will go thru the above procedure, but will have no
+ // real effect
+ $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ * (WARNING: using names that require this is a REALLY BAD IDEA)
+ *
+ * WARNING: Older versions of MySQL can't handle the backtick
+ * character (<kbd>`</kbd>) in table or column names.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '`' . str_replace('`', '``', $str) . '`';
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return @mysqli_real_escape_string($this->connection, $str);
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query) || $this->_next_query_manip) {
+ return $query . " LIMIT $count";
+ } else {
+ return $query . " LIMIT $from, $count";
+ }
+ }
+
+ // }}}
+ // {{{ mysqliRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mysqli::errorNative(), DB_common::errorCode()
+ */
+ function mysqliRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
+ $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
+ }
+ $errno = $this->errorCode(mysqli_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @mysqli_errno($this->connection) . ' ** ' .
+ @mysqli_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @mysqli_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ // Fix for bug #11580.
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @mysqli_query($this->connection,
+ "SELECT * FROM $result LIMIT 0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_a($id, 'mysqli_result')) {
+ return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mysqli_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $tmp = @mysqli_fetch_field($id);
+
+ $flags = '';
+ foreach ($this->mysqli_flags as $const => $means) {
+ if ($tmp->flags & $const) {
+ $flags .= $means . ' ';
+ }
+ }
+ if ($tmp->def) {
+ $flags .= 'default_' . rawurlencode($tmp->def);
+ }
+ $flags = trim($flags);
+
+ $res[$i] = array(
+ 'table' => $case_func($tmp->table),
+ 'name' => $case_func($tmp->name),
+ 'type' => isset($this->mysqli_types[$tmp->type])
+ ? $this->mysqli_types[$tmp->type]
+ : 'unknown',
+ // http://bugs.php.net/?id=36579
+ 'len' => $tmp->length,
+ 'flags' => $flags,
+ );
+
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mysqli_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SHOW TABLES';
+ case 'users':
+ return 'SELECT DISTINCT User FROM mysql.user';
+ case 'databases':
+ return 'SHOW DATABASES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/oci8.php b/_darcs/pristine/extlib/DB/oci8.php
new file mode 100644
index 000000000..d30794871
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/oci8.php
@@ -0,0 +1,1156 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's oci8 extension
+ * for interacting with Oracle databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author James L. Pine <jlp@valinux.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: oci8.php,v 1.116 2007/11/28 02:22:39 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's oci8 extension
+ * for interacting with Oracle databases
+ *
+ * Definitely works with versions 8 and 9 of Oracle.
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * Be aware... OCIError() only appears to return anything when given a
+ * statement, so functions return the generic DB_ERROR instead of more
+ * useful errors that have to do with feedback from the database.
+ *
+ * @category Database
+ * @package DB
+ * @author James L. Pine <jlp@valinux.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_oci8 extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'oci8';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'oci8';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '5.0.0',
+ 'numrows' => 'subquery',
+ 'pconnect' => true,
+ 'prepare' => true,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1 => DB_ERROR_CONSTRAINT,
+ 900 => DB_ERROR_SYNTAX,
+ 904 => DB_ERROR_NOSUCHFIELD,
+ 913 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 921 => DB_ERROR_SYNTAX,
+ 923 => DB_ERROR_SYNTAX,
+ 942 => DB_ERROR_NOSUCHTABLE,
+ 955 => DB_ERROR_ALREADY_EXISTS,
+ 1400 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 1401 => DB_ERROR_INVALID,
+ 1407 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 1418 => DB_ERROR_NOT_FOUND,
+ 1476 => DB_ERROR_DIVZERO,
+ 1722 => DB_ERROR_INVALID_NUMBER,
+ 2289 => DB_ERROR_NOSUCHTABLE,
+ 2291 => DB_ERROR_CONSTRAINT,
+ 2292 => DB_ERROR_CONSTRAINT,
+ 2449 => DB_ERROR_CONSTRAINT,
+ 12899 => DB_ERROR_INVALID,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * Stores the $data passed to execute() in the oci8 driver
+ *
+ * Gets reset to array() when simpleQuery() is run.
+ *
+ * Needed in case user wants to call numRows() after prepare/execute
+ * was used.
+ *
+ * @var array
+ * @access private
+ */
+ var $_data = array();
+
+ /**
+ * The result or statement handle from the most recently executed query
+ * @var resource
+ */
+ var $last_stmt;
+
+ /**
+ * Is the given prepared statement a data manipulation query?
+ * @var array
+ * @access private
+ */
+ var $manip_query = array();
+
+ /**
+ * Store of prepared SQL queries.
+ * @var array
+ * @access private
+ */
+ var $_prepared_queries = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_oci8()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * If PHP is at version 5.0.0 or greater:
+ * + Generally, oci_connect() or oci_pconnect() are used.
+ * + But if the new_link DSN option is set to true, oci_new_connect()
+ * is used.
+ *
+ * When using PHP version 4.x, OCILogon() or OCIPLogon() are used.
+ *
+ * PEAR DB's oci8 driver supports the following extra DSN options:
+ * + charset The character set to be used on the connection.
+ * Only used if PHP is at version 5.0.0 or greater
+ * and the Oracle server is at 9.2 or greater.
+ * Available since PEAR DB 1.7.0.
+ * + new_link If set to true, causes subsequent calls to
+ * connect() to return a new connection link
+ * instead of the existing one. WARNING: this is
+ * not portable to other DBMS's.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('oci8')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ // Backwards compatibility with DB < 1.7.0
+ if (empty($dsn['database']) && !empty($dsn['hostspec'])) {
+ $db = $dsn['hostspec'];
+ } else {
+ $db = $dsn['database'];
+ }
+
+ if (function_exists('oci_connect')) {
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ $connect_function = 'oci_new_connect';
+ } else {
+ $connect_function = $persistent ? 'oci_pconnect'
+ : 'oci_connect';
+ }
+ if (isset($this->dsn['port']) && $this->dsn['port']) {
+ $db = '//'.$db.':'.$this->dsn['port'];
+ }
+
+ $char = empty($dsn['charset']) ? null : $dsn['charset'];
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password'],
+ $db,
+ $char);
+ $error = OCIError();
+ if (!empty($error) && $error['code'] == 12541) {
+ // Couldn't find TNS listener. Try direct connection.
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password'],
+ null,
+ $char);
+ }
+ } else {
+ $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
+ if ($db) {
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password'],
+ $db);
+ } elseif ($dsn['username'] || $dsn['password']) {
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password']);
+ }
+ }
+
+ if (!$this->connection) {
+ $error = OCIError();
+ $error = (is_array($error)) ? $error['message'] : null;
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $error);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ if (function_exists('oci_close')) {
+ $ret = @oci_close($this->connection);
+ } else {
+ $ret = @OCILogOff($this->connection);
+ }
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * To determine how many rows of a result set get buffered using
+ * ocisetprefetch(), see the "result_buffering" option in setOptions().
+ * This option was added in Release 1.7.0.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->_data = array();
+ $this->last_parameters = array();
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @OCIParse($this->connection, $query);
+ if (!$result) {
+ return $this->oci8RaiseError();
+ }
+ if ($this->autocommit) {
+ $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS);
+ } else {
+ $success = @OCIExecute($result,OCI_DEFAULT);
+ }
+ if (!$success) {
+ return $this->oci8RaiseError($result);
+ }
+ $this->last_stmt = $result;
+ if ($this->_checkManip($query)) {
+ return DB_OK;
+ } else {
+ @ocisetprefetch($result, $this->options['result_buffering']);
+ return $result;
+ }
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal oracle result pointer to the next available result
+ *
+ * @param a valid oci8 result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE &&
+ $moredata)
+ {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+ }
+ if (!$moredata) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? OCIFreeStatement($result) : false;
+ }
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_oci8::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ if (!is_resource($stmt)) {
+ return false;
+ }
+ if ($free_resource) {
+ @ocifreestatement($stmt);
+ }
+ if (isset($this->prepare_types[(int)$stmt])) {
+ unset($this->prepare_types[(int)$stmt]);
+ unset($this->manip_query[(int)$stmt]);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * Only works if the DB_PORTABILITY_NUMROWS portability option
+ * is turned on.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows(), DB_common::setOption()
+ */
+ function numRows($result)
+ {
+ // emulate numRows for Oracle. yuck.
+ if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
+ $result === $this->last_stmt)
+ {
+ $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')';
+ $save_query = $this->last_query;
+ $save_stmt = $this->last_stmt;
+
+ $count = $this->query($countquery);
+
+ // Restore the last query and statement.
+ $this->last_query = $save_query;
+ $this->last_stmt = $save_stmt;
+
+ if (DB::isError($count) ||
+ DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED)))
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ return $row[0];
+ }
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @OCINumCols($result);
+ if (!$cols) {
+ return $this->oci8RaiseError($result);
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute().
+ *
+ * With oci8, this is emulated.
+ *
+ * prepare() requires a generic query as string like <code>
+ * INSERT INTO numbers VALUES (?, ?, ?)
+ * </code>. The <kbd>?</kbd> characters are placeholders.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders. Example: <code>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </code>
+ *
+ * @param string $query the query to be prepared
+ *
+ * @return mixed DB statement resource on success. DB_Error on failure.
+ *
+ * @see DB_oci8::execute()
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $binds = count($tokens) - 1;
+ $token = 0;
+ $types = array();
+ $newquery = '';
+
+ foreach ($tokens as $key => $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ unset($tokens[$key]);
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ unset($tokens[$key]);
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ unset($tokens[$key]);
+ break;
+ default:
+ $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ if ($key != $binds) {
+ $newquery .= $tokens[$key] . ':bind' . $token;
+ } else {
+ $newquery .= $tokens[$key];
+ }
+ }
+ }
+
+ $this->last_query = $query;
+ $newquery = $this->modifyQuery($newquery);
+ if (!$stmt = @OCIParse($this->connection, $newquery)) {
+ return $this->oci8RaiseError();
+ }
+ $this->prepare_types[(int)$stmt] = $types;
+ $this->manip_query[(int)$stmt] = DB::isManip($query);
+ $this->_prepared_queries[(int)$stmt] = $newquery;
+ return $stmt;
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare().
+ *
+ * To determine how many rows of a result set get buffered using
+ * ocisetprefetch(), see the "result_buffering" option in setOptions().
+ * This option was added in Release 1.7.0.
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 for non-array items or the
+ * quantity of elements in the array.
+ *
+ * @return mixed returns an oic8 result resource for successful SELECT
+ * queries, DB_OK for other successful queries.
+ * A DB error object is returned on failure.
+ *
+ * @see DB_oci8::prepare()
+ */
+ function &execute($stmt, $data = array())
+ {
+ $data = (array)$data;
+ $this->last_parameters = $data;
+ $this->last_query = $this->_prepared_queries[(int)$stmt];
+ $this->_data = $data;
+
+ $types = $this->prepare_types[(int)$stmt];
+ if (count($types) != count($data)) {
+ $tmp = $this->raiseError(DB_ERROR_MISMATCH);
+ return $tmp;
+ }
+
+ $i = 0;
+ foreach ($data as $key => $value) {
+ if ($types[$i] == DB_PARAM_MISC) {
+ /*
+ * Oracle doesn't seem to have the ability to pass a
+ * parameter along unchanged, so strip off quotes from start
+ * and end, plus turn two single quotes to one single quote,
+ * in order to avoid the quotes getting escaped by
+ * Oracle and ending up in the database.
+ */
+ $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
+ $data[$key] = str_replace("''", "'", $data[$key]);
+ } elseif ($types[$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($data[$key], 'rb');
+ if (!$fp) {
+ $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ return $tmp;
+ }
+ $data[$key] = fread($fp, filesize($data[$key]));
+ fclose($fp);
+ } elseif ($types[$i] == DB_PARAM_SCALAR) {
+ // Floats have to be converted to a locale-neutral
+ // representation.
+ if (is_float($data[$key])) {
+ $data[$key] = $this->quoteFloat($data[$key]);
+ }
+ }
+ if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) {
+ $tmp = $this->oci8RaiseError($stmt);
+ return $tmp;
+ }
+ $this->last_query = preg_replace("/:bind$i/",$this->quoteSmart($data[$key]),$this->last_query,1);
+ $i++;
+ }
+ if ($this->autocommit) {
+ $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
+ } else {
+ $success = @OCIExecute($stmt, OCI_DEFAULT);
+ }
+ if (!$success) {
+ $tmp = $this->oci8RaiseError($stmt);
+ return $tmp;
+ }
+ $this->last_stmt = $stmt;
+ if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
+ $this->_last_query_manip = true;
+ $this->_next_query_manip = false;
+ $tmp = DB_OK;
+ } else {
+ $this->_last_query_manip = false;
+ @ocisetprefetch($stmt, $this->options['result_buffering']);
+ $tmp = new DB_result($this, $stmt);
+ }
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ $this->autocommit = (bool)$onoff;;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ $result = @OCICommit($this->connection);
+ if (!$result) {
+ return $this->oci8RaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ $result = @OCIRollback($this->connection);
+ if (!$result) {
+ return $this->oci8RaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->last_stmt === false) {
+ return $this->oci8RaiseError();
+ }
+ $result = @OCIRowCount($this->last_stmt);
+ if ($result === false) {
+ return $this->oci8RaiseError($this->last_stmt);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ */
+ function modifyQuery($query)
+ {
+ if (preg_match('/^\s*SELECT/i', $query) &&
+ !preg_match('/\sFROM\s/i', $query)) {
+ $query .= ' FROM dual';
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ // Let Oracle return the name of the columns instead of
+ // coding a "home" SQL parser
+
+ if (count($params)) {
+ $result = $this->prepare("SELECT * FROM ($query) "
+ . 'WHERE NULL = NULL');
+ $tmp = $this->execute($result, $params);
+ } else {
+ $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
+
+ if (!$result = @OCIParse($this->connection, $q_fields)) {
+ $this->last_query = $q_fields;
+ return $this->oci8RaiseError();
+ }
+ if (!@OCIExecute($result, OCI_DEFAULT)) {
+ $this->last_query = $q_fields;
+ return $this->oci8RaiseError($result);
+ }
+ }
+
+ $ncols = OCINumCols($result);
+ $cols = array();
+ for ( $i = 1; $i <= $ncols; $i++ ) {
+ $cols[] = '"' . OCIColumnName($result, $i) . '"';
+ }
+ $fields = implode(', ', $cols);
+ // XXX Test that (tip by John Lim)
+ //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) {
+ // // Introduce the FIRST_ROWS Oracle query optimizer
+ // $query = substr($query, strlen($match[0]), strlen($query));
+ // $query = "SELECT /* +FIRST_ROWS */ " . $query;
+ //}
+
+ // Construct the query
+ // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
+ // Perhaps this could be optimized with the use of Unions
+ $query = "SELECT $fields FROM".
+ " (SELECT rownum as linenum, $fields FROM".
+ " ($query)".
+ ' WHERE rownum <= '. ($from + $count) .
+ ') WHERE linenum >= ' . ++$from;
+ return $query;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_oci8::createSequence(), DB_oci8::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = 0;
+ do {
+ $this->expectError(DB_ERROR_NOSUCHTABLE);
+ $result = $this->query("SELECT ${seqname}.nextval FROM dual");
+ $this->popExpect();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ return $arr[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_oci8::nextID(), DB_oci8::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_oci8::nextID(), DB_oci8::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ oci8RaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_oci8::errorNative(), DB_oci8::errorCode()
+ */
+ function oci8RaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $error = @OCIError($this->connection);
+ return $this->raiseError($this->errorCode($error['code']),
+ null, null, null, $error['message']);
+ } elseif (is_resource($errno)) {
+ $error = @OCIError($errno);
+ return $this->raiseError($this->errorCode($error['code']),
+ null, null, null, $error['message']);
+ }
+ return $this->raiseError($this->errorCode($errno));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code. FALSE if the code could not be
+ * determined
+ */
+ function errorNative()
+ {
+ if (is_resource($this->last_stmt)) {
+ $error = @OCIError($this->last_stmt);
+ } else {
+ $error = @OCIError($this->connection);
+ }
+ if (is_array($error)) {
+ return $error['code'];
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * NOTE: flags won't contain index information.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $res = array();
+
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $result = strtoupper($result);
+ $q_fields = 'SELECT column_name, data_type, data_length, '
+ . 'nullable '
+ . 'FROM user_tab_columns '
+ . "WHERE table_name='$result' ORDER BY column_id";
+
+ $this->last_query = $q_fields;
+
+ if (!$stmt = @OCIParse($this->connection, $q_fields)) {
+ return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+ if (!@OCIExecute($stmt, OCI_DEFAULT)) {
+ return $this->oci8RaiseError($stmt);
+ }
+
+ $i = 0;
+ while (@OCIFetch($stmt)) {
+ $res[$i] = array(
+ 'table' => $case_func($result),
+ 'name' => $case_func(@OCIResult($stmt, 1)),
+ 'type' => @OCIResult($stmt, 2),
+ 'len' => @OCIResult($stmt, 3),
+ 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ $i++;
+ }
+
+ if ($mode) {
+ $res['num_fields'] = $i;
+ }
+ @OCIFreeStatement($stmt);
+
+ } else {
+ if (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $result = $result->result;
+ }
+
+ $res = array();
+
+ if ($result === $this->last_stmt) {
+ $count = @OCINumCols($result);
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => '',
+ 'name' => $case_func(@OCIColumnName($result, $i+1)),
+ 'type' => @OCIColumnType($result, $i+1),
+ 'len' => @OCIColumnSize($result, $i+1),
+ 'flags' => '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+ } else {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT table_name FROM user_tables';
+ case 'synonyms':
+ return 'SELECT synonym_name FROM user_synonyms';
+ case 'views':
+ return 'SELECT view_name FROM user_views';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+ // {{{ quoteFloat()
+
+ /**
+ * Formats a float value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param float the float value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteFloat($float) {
+ return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/odbc.php b/_darcs/pristine/extlib/DB/odbc.php
new file mode 100644
index 000000000..eba43659a
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/odbc.php
@@ -0,0 +1,883 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's odbc extension
+ * for interacting with databases via ODBC connections
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: odbc.php,v 1.81 2007/07/06 05:19:21 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's odbc extension
+ * for interacting with databases via ODBC connections
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * More info on ODBC errors could be found here:
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_odbc extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'odbc';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'sql92';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * NOTE: The feature set of the following drivers are different than
+ * the default:
+ * + solid: 'transactions' = true
+ * + navision: 'limit' = false
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ '01004' => DB_ERROR_TRUNCATED,
+ '07001' => DB_ERROR_MISMATCH,
+ '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '21S02' => DB_ERROR_MISMATCH,
+ '22001' => DB_ERROR_INVALID,
+ '22003' => DB_ERROR_INVALID_NUMBER,
+ '22005' => DB_ERROR_INVALID_NUMBER,
+ '22008' => DB_ERROR_INVALID_DATE,
+ '22012' => DB_ERROR_DIVZERO,
+ '23000' => DB_ERROR_CONSTRAINT,
+ '23502' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '23503' => DB_ERROR_CONSTRAINT,
+ '23504' => DB_ERROR_CONSTRAINT,
+ '23505' => DB_ERROR_CONSTRAINT,
+ '24000' => DB_ERROR_INVALID,
+ '34000' => DB_ERROR_INVALID,
+ '37000' => DB_ERROR_SYNTAX,
+ '42000' => DB_ERROR_SYNTAX,
+ '42601' => DB_ERROR_SYNTAX,
+ 'IM001' => DB_ERROR_UNSUPPORTED,
+ 'S0000' => DB_ERROR_NOSUCHTABLE,
+ 'S0001' => DB_ERROR_ALREADY_EXISTS,
+ 'S0002' => DB_ERROR_NOSUCHTABLE,
+ 'S0011' => DB_ERROR_ALREADY_EXISTS,
+ 'S0012' => DB_ERROR_NOT_FOUND,
+ 'S0021' => DB_ERROR_ALREADY_EXISTS,
+ 'S0022' => DB_ERROR_NOSUCHFIELD,
+ 'S1009' => DB_ERROR_INVALID,
+ 'S1090' => DB_ERROR_INVALID,
+ 'S1C00' => DB_ERROR_NOT_CAPABLE,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ * @access private
+ */
+ var $affected = 0;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_odbc()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's odbc driver supports the following extra DSN options:
+ * + cursor The type of cursor to be used for this connection.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('odbc')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+ switch ($this->dbsyntax) {
+ case 'access':
+ case 'db2':
+ case 'solid':
+ $this->features['transactions'] = true;
+ break;
+ case 'navision':
+ $this->features['limit'] = false;
+ }
+
+ /*
+ * This is hear for backwards compatibility. Should have been using
+ * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used.
+ */
+ if ($dsn['database']) {
+ $odbcdsn = $dsn['database'];
+ } elseif ($dsn['hostspec']) {
+ $odbcdsn = $dsn['hostspec'];
+ } else {
+ $odbcdsn = 'localhost';
+ }
+
+ $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
+
+ if (empty($dsn['cursor'])) {
+ $this->connection = @$connect_function($odbcdsn, $dsn['username'],
+ $dsn['password']);
+ } else {
+ $this->connection = @$connect_function($odbcdsn, $dsn['username'],
+ $dsn['password'],
+ $dsn['cursor']);
+ }
+
+ if (!is_resource($this->connection)) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $this->errorNative());
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $err = @odbc_close($this->connection);
+ $this->connection = null;
+ return $err;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @odbc_exec($this->connection, $query);
+ if (!$result) {
+ return $this->odbcRaiseError(); // XXX ERRORMSG
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if ($this->_checkManip($query)) {
+ $this->affected = $result; // For affectedRows()
+ return DB_OK;
+ }
+ $this->affected = 0;
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal odbc result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return @odbc_next_result($result);
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ $arr = array();
+ if ($rownum !== null) {
+ $rownum++; // ODBC first row is 1
+ if (version_compare(phpversion(), '4.2.0', 'ge')) {
+ $cols = @odbc_fetch_into($result, $arr, $rownum);
+ } else {
+ $cols = @odbc_fetch_into($result, $rownum, $arr);
+ }
+ } else {
+ $cols = @odbc_fetch_into($result, $arr);
+ }
+ if (!$cols) {
+ return null;
+ }
+ if ($fetchmode !== DB_FETCHMODE_ORDERED) {
+ for ($i = 0; $i < count($arr); $i++) {
+ $colName = @odbc_field_name($result, $i+1);
+ $a[$colName] = $arr[$i];
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $a = array_change_key_case($a, CASE_LOWER);
+ }
+ $arr = $a;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? odbc_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @odbc_num_fields($result);
+ if (!$cols) {
+ return $this->odbcRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (empty($this->affected)) { // In case of SELECT stms
+ return 0;
+ }
+ $nrows = @odbc_num_rows($this->affected);
+ if ($nrows == -1) {
+ return $this->odbcRaiseError();
+ }
+ return $nrows;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * Not all ODBC drivers support this functionality. If they don't
+ * a DB_Error object for DB_ERROR_UNSUPPORTED is returned.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $nrows = @odbc_num_rows($result);
+ if ($nrows == -1) {
+ return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
+ }
+ if ($nrows === false) {
+ return $this->odbcRaiseError();
+ }
+ return $nrows;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked
+ * "Use ANSI quoted identifiers" when setting up the ODBC data source.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ switch ($this->dsn['dbsyntax']) {
+ case 'access':
+ return '[' . $str . ']';
+ case 'mssql':
+ case 'sybase':
+ return '[' . str_replace(']', ']]', $str) . ']';
+ case 'mysql':
+ case 'mysqli':
+ return '`' . $str . '`';
+ default:
+ return '"' . str_replace('"', '""', $str) . '"';
+ }
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ * @internal
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_odbc::createSequence(), DB_odbc::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("update ${seqname} set id = id + 1");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = 1;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $result = $this->query("insert into ${seqname} (id) values(0)");
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ $result = $this->query("select id from ${seqname}");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ if (DB::isError($row || !$row)) {
+ return $row;
+ }
+
+ return $row[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_odbc::nextID(), DB_odbc::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE TABLE '
+ . $this->getSequenceName($seq_name)
+ . ' (id integer NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_odbc::nextID(), DB_odbc::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ if (!@odbc_autocommit($this->connection, $onoff)) {
+ return $this->odbcRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if (!@odbc_commit($this->connection)) {
+ return $this->odbcRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if (!@odbc_rollback($this->connection)) {
+ return $this->odbcRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ odbcRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_odbc::errorNative(), DB_common::errorCode()
+ */
+ function odbcRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ switch ($this->dbsyntax) {
+ case 'access':
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
+ }
+
+ $native_code = odbc_error($this->connection);
+
+ // S1000 is for "General Error." Let's be more specific.
+ if ($native_code == 'S1000') {
+ $errormsg = odbc_errormsg($this->connection);
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/includes related records.$/i' => DB_ERROR_CONSTRAINT,
+ '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $this->raiseError($code,
+ null, null, null,
+ $native_code . ' ' . $errormsg);
+ }
+ }
+ $errno = DB_ERROR;
+ } else {
+ $errno = $this->errorCode($native_code);
+ }
+ break;
+ default:
+ $errno = $this->errorCode(odbc_error($this->connection));
+ }
+ }
+ return $this->raiseError($errno, null, null, null,
+ $this->errorNative());
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code and message produced by the last query
+ *
+ * @return string the DBMS' error code and message
+ */
+ function errorNative()
+ {
+ if (!is_resource($this->connection)) {
+ return @odbc_error() . ' ' . @odbc_errormsg();
+ }
+ return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @odbc_exec($this->connection, "SELECT * FROM $result");
+ if (!$id) {
+ return $this->odbcRaiseError();
+ }
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @odbc_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $col = $i + 1;
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@odbc_field_name($id, $col)),
+ 'type' => @odbc_field_type($id, $col),
+ 'len' => @odbc_field_len($id, $col),
+ 'flags' => '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @odbc_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the list of objects requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ * @since Method available since Release 1.7.0
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'databases':
+ if (!function_exists('odbc_data_source')) {
+ return null;
+ }
+ $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST);
+ if (is_array($res)) {
+ $out = array($res['server']);
+ while($res = @odbc_data_source($this->connection,
+ SQL_FETCH_NEXT))
+ {
+ $out[] = $res['server'];
+ }
+ return $out;
+ } else {
+ return $this->odbcRaiseError();
+ }
+ break;
+ case 'tables':
+ case 'schema.tables':
+ $keep = 'TABLE';
+ break;
+ case 'views':
+ $keep = 'VIEW';
+ break;
+ default:
+ return null;
+ }
+
+ /*
+ * Removing non-conforming items in the while loop rather than
+ * in the odbc_tables() call because some backends choke on this:
+ * odbc_tables($this->connection, '', '', '', 'TABLE')
+ */
+ $res = @odbc_tables($this->connection);
+ if (!$res) {
+ return $this->odbcRaiseError();
+ }
+ $out = array();
+ while ($row = odbc_fetch_array($res)) {
+ if ($row['TABLE_TYPE'] != $keep) {
+ continue;
+ }
+ if ($type == 'schema.tables') {
+ $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME'];
+ } else {
+ $out[] = $row['TABLE_NAME'];
+ }
+ }
+ return $out;
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/pgsql.php b/_darcs/pristine/extlib/DB/pgsql.php
new file mode 100644
index 000000000..6030bb4c1
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/pgsql.php
@@ -0,0 +1,1135 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's pgsql extension
+ * for interacting with PostgreSQL databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Rui Hirokawa <hirokawa@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: pgsql.php,v 1.139 2007/11/28 02:19:44 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's pgsql extension
+ * for interacting with PostgreSQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Rui Hirokawa <hirokawa@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_pgsql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'pgsql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'pgsql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '4.3.0',
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => true,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ */
+ var $affected = 0;
+
+ /**
+ * The current row being looked at in fetchInto()
+ * @var array
+ * @access private
+ */
+ var $row = array();
+
+ /**
+ * The number of rows in a given result set
+ * @var array
+ * @access private
+ */
+ var $_num_rows = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_pgsql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's pgsql driver supports the following extra DSN options:
+ * + connect_timeout How many seconds to wait for a connection to
+ * be established. Available since PEAR DB 1.7.0.
+ * + new_link If set to true, causes subsequent calls to
+ * connect() to return a new connection link
+ * instead of the existing one. WARNING: this is
+ * not portable to other DBMS's. Available only
+ * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0.
+ * + options Command line options to be sent to the server.
+ * Available since PEAR DB 1.6.4.
+ * + service Specifies a service name in pg_service.conf that
+ * holds additional connection parameters.
+ * Available since PEAR DB 1.7.0.
+ * + sslmode How should SSL be used when connecting? Values:
+ * disable, allow, prefer or require.
+ * Available since PEAR DB 1.7.0.
+ * + tty This was used to specify where to send server
+ * debug output. Available since PEAR DB 1.6.4.
+ *
+ * Example of connecting to a new link via a socket:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db = DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('pgsql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp';
+
+ $params = array('');
+ if ($protocol == 'tcp') {
+ if ($dsn['hostspec']) {
+ $params[0] .= 'host=' . $dsn['hostspec'];
+ }
+ if ($dsn['port']) {
+ $params[0] .= ' port=' . $dsn['port'];
+ }
+ } elseif ($protocol == 'unix') {
+ // Allow for pg socket in non-standard locations.
+ if ($dsn['socket']) {
+ $params[0] .= 'host=' . $dsn['socket'];
+ }
+ if ($dsn['port']) {
+ $params[0] .= ' port=' . $dsn['port'];
+ }
+ }
+ if ($dsn['database']) {
+ $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\'';
+ }
+ if ($dsn['username']) {
+ $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\'';
+ }
+ if ($dsn['password']) {
+ $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\'';
+ }
+ if (!empty($dsn['options'])) {
+ $params[0] .= ' options=' . $dsn['options'];
+ }
+ if (!empty($dsn['tty'])) {
+ $params[0] .= ' tty=' . $dsn['tty'];
+ }
+ if (!empty($dsn['connect_timeout'])) {
+ $params[0] .= ' connect_timeout=' . $dsn['connect_timeout'];
+ }
+ if (!empty($dsn['sslmode'])) {
+ $params[0] .= ' sslmode=' . $dsn['sslmode'];
+ }
+ if (!empty($dsn['service'])) {
+ $params[0] .= ' service=' . $dsn['service'];
+ }
+
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ if (version_compare(phpversion(), '4.3.0', '>=')) {
+ $params[] = PGSQL_CONNECT_FORCE_NEW;
+ }
+ }
+
+ $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ @ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ @ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @pg_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @pg_exec($this->connection, 'begin;');
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @pg_exec($this->connection, $query);
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+
+ /*
+ * Determine whether queries produce affected rows, result or nothing.
+ *
+ * This logic was introduced in version 1.1 of the file by ssb,
+ * though the regex has been modified slightly since then.
+ *
+ * PostgreSQL commands:
+ * ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
+ * CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
+ * GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
+ * REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
+ * UNLISTEN, UPDATE, VACUUM
+ */
+ if ($ismanip) {
+ $this->affected = @pg_affected_rows($result);
+ return DB_OK;
+ } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW)\s/si',
+ $query))
+ {
+ $this->row[(int)$result] = 0; // reset the row counter.
+ $numrows = $this->numRows($result);
+ if (is_object($numrows)) {
+ return $numrows;
+ }
+ $this->_num_rows[(int)$result] = $numrows;
+ $this->affected = 0;
+ return $result;
+ } else {
+ $this->affected = 0;
+ return DB_OK;
+ }
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal pgsql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ $result_int = (int)$result;
+ $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
+ if ($rownum >= $this->_num_rows[$result_int]) {
+ return null;
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @pg_fetch_row($result, $rownum);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ $this->row[$result_int] = ++$rownum;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ if (is_resource($result)) {
+ unset($this->row[(int)$result]);
+ unset($this->_num_rows[(int)$result]);
+ $this->affected = 0;
+ return @pg_freeresult($result);
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ * @internal
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ quoteBoolean()
+
+ /**
+ * Formats a boolean value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param boolean the boolean value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteBoolean($boolean) {
+ return $boolean ? 'TRUE' : 'FALSE';
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * {@internal PostgreSQL treats a backslash as an escape character,
+ * so they are escaped as well.
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ if (function_exists('pg_escape_string')) {
+ /* This fixes an undocumented BC break in PHP 5.2.0 which changed
+ * the prototype of pg_escape_string. I'm not thrilled about having
+ * to sniff the PHP version, quite frankly, but it's the only way
+ * to deal with the problem. Revision 1.331.2.13.2.10 on
+ * php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the
+ * record. */
+ if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
+ return pg_escape_string($this->connection, $str);
+ } else {
+ return pg_escape_string($str);
+ }
+ } else {
+ return str_replace("'", "''", str_replace('\\', '\\\\', $str));
+ }
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @pg_numfields($result);
+ if (!$cols) {
+ return $this->pgsqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @pg_numrows($result);
+ if ($rows === null) {
+ return $this->pgsqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ // (disabled) hack to shut up error messages from libpq.a
+ //@fclose(@fopen("php://stderr", "w"));
+ $result = @pg_exec($this->connection, 'end;');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @pg_exec($this->connection, 'abort;');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return $this->affected;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_pgsql::createSequence(), DB_pgsql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = false;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("SELECT NEXTVAL('${seqname}')");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = true;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_pgsql::nextID(), DB_pgsql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $result = $this->query("CREATE SEQUENCE ${seqname}");
+ return $result;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_pgsql::nextID(), DB_pgsql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return "$query LIMIT $count OFFSET $from";
+ }
+
+ // }}}
+ // {{{ pgsqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_pgsql::errorNative(), DB_pgsql::errorCode()
+ */
+ function pgsqlRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if (!$native) {
+ $native = 'Database connection has been lost.';
+ $errno = DB_ERROR_CONNECT_FAILED;
+ }
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * {@internal Error messages are used instead of error codes
+ * in order to support older versions of PostgreSQL.}}
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @pg_errormessage($this->connection);
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message.
+ *
+ * @param string $errormsg error message returned from the database
+ * @return integer an error number from a DB error constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/column .* (of relation .*)?does not exist/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/(relation|sequence|table).*does not exist|class .* not found/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/index .* does not exist/'
+ => DB_ERROR_NOT_FOUND,
+ '/relation .* already exists/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/(divide|division) by zero$/i'
+ => DB_ERROR_DIVZERO,
+ '/pg_atoi: error in .*: can\'t parse /i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/invalid input syntax for( type)? (integer|numeric)/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/value .* is out of range for type \w*int/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/integer out of range/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/value too long for type character/i'
+ => DB_ERROR_INVALID,
+ '/attribute .* not found|relation .* does not have attribute/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/column .* specified in USING clause does not exist in (left|right) table/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/parser: parse error at or near/i'
+ => DB_ERROR_SYNTAX,
+ '/syntax error at/'
+ => DB_ERROR_SYNTAX,
+ '/column reference .* is ambiguous/i'
+ => DB_ERROR_SYNTAX,
+ '/permission denied/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/violates not-null constraint/'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/violates [\w ]+ constraint/'
+ => DB_ERROR_CONSTRAINT,
+ '/referential integrity violation/'
+ => DB_ERROR_CONSTRAINT,
+ '/more expressions than target columns/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @pg_numfields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@pg_fieldname($id, $i)),
+ 'type' => @pg_fieldtype($id, $i),
+ 'len' => @pg_fieldsize($id, $i),
+ 'flags' => $got_string
+ ? $this->_pgFieldFlags($id, $i, $result)
+ : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @pg_freeresult($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _pgFieldFlags()
+
+ /**
+ * Get a column's flags
+ *
+ * Supports "not_null", "default_value", "primary_key", "unique_key"
+ * and "multiple_key". The default value is passed through
+ * rawurlencode() in case there are spaces in it.
+ *
+ * @param int $resource the PostgreSQL result identifier
+ * @param int $num_field the field number
+ *
+ * @return string the flags
+ *
+ * @access private
+ */
+ function _pgFieldFlags($resource, $num_field, $table_name)
+ {
+ $field_name = @pg_fieldname($resource, $num_field);
+
+ // Check if there's a schema in $table_name and update things
+ // accordingly.
+ $from = 'pg_attribute f, pg_class tab, pg_type typ';
+ if (strpos($table_name, '.') !== false) {
+ $from .= ', pg_namespace nsp';
+ list($schema, $table) = explode('.', $table_name);
+ $tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'";
+ } else {
+ $tableWhere = "tab.relname = '$table_name'";
+ }
+
+ $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
+ FROM $from
+ WHERE tab.relname = typ.typname
+ AND typ.typrelid = f.attrelid
+ AND f.attname = '$field_name'
+ AND $tableWhere");
+ if (@pg_numrows($result) > 0) {
+ $row = @pg_fetch_row($result, 0);
+ $flags = ($row[0] == 't') ? 'not_null ' : '';
+
+ if ($row[1] == 't') {
+ $result = @pg_exec($this->connection, "SELECT a.adsrc
+ FROM $from, pg_attrdef a
+ WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
+ AND f.attrelid = a.adrelid AND f.attname = '$field_name'
+ AND $tableWhere AND f.attnum = a.adnum");
+ $row = @pg_fetch_row($result, 0);
+ $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
+ $flags .= 'default_' . rawurlencode($num) . ' ';
+ }
+ } else {
+ $flags = '';
+ }
+ $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
+ FROM $from, pg_index i
+ WHERE tab.relname = typ.typname
+ AND typ.typrelid = f.attrelid
+ AND f.attrelid = i.indrelid
+ AND f.attname = '$field_name'
+ AND $tableWhere");
+ $count = @pg_numrows($result);
+
+ for ($i = 0; $i < $count ; $i++) {
+ $row = @pg_fetch_row($result, $i);
+ $keys = explode(' ', $row[2]);
+
+ if (in_array($num_field + 1, $keys)) {
+ $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
+ $flags .= ($row[1] == 't') ? 'primary_key ' : '';
+ if (count($keys) > 1)
+ $flags .= 'multiple_key ';
+ }
+ }
+
+ return trim($flags);
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT c.relname AS "Name"'
+ . ' FROM pg_class c, pg_user u'
+ . ' WHERE c.relowner = u.usesysid'
+ . " AND c.relkind = 'r'"
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_views'
+ . ' WHERE viewname = c.relname)'
+ . " AND c.relname !~ '^(pg_|sql_)'"
+ . ' UNION'
+ . ' SELECT c.relname AS "Name"'
+ . ' FROM pg_class c'
+ . " WHERE c.relkind = 'r'"
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_views'
+ . ' WHERE viewname = c.relname)'
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_user'
+ . ' WHERE usesysid = c.relowner)'
+ . " AND c.relname !~ '^pg_'";
+ case 'schema.tables':
+ return "SELECT schemaname || '.' || tablename"
+ . ' AS "Name"'
+ . ' FROM pg_catalog.pg_tables'
+ . ' WHERE schemaname NOT IN'
+ . " ('pg_catalog', 'information_schema', 'pg_toast')";
+ case 'schema.views':
+ return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname"
+ . " NOT IN ('information_schema', 'pg_catalog')";
+ case 'views':
+ // Table cols: viewname | viewowner | definition
+ return 'SELECT viewname from pg_views WHERE schemaname'
+ . " NOT IN ('information_schema', 'pg_catalog')";
+ case 'users':
+ // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil
+ return 'SELECT usename FROM pg_user';
+ case 'databases':
+ return 'SELECT datname FROM pg_database';
+ case 'functions':
+ case 'procedures':
+ return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+ // {{{ _checkManip()
+
+ /**
+ * Checks if the given query is a manipulation query. This also takes into
+ * account the _next_query_manip flag and sets the _last_query_manip flag
+ * (and resets _next_query_manip) according to the result.
+ *
+ * @param string The query to check.
+ *
+ * @return boolean true if the query is a manipulation query, false
+ * otherwise
+ *
+ * @access protected
+ */
+ function _checkManip($query)
+ {
+ return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query)
+ || parent::_checkManip($query));
+ }
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/sqlite.php b/_darcs/pristine/extlib/DB/sqlite.php
new file mode 100644
index 000000000..5c4b396e5
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/sqlite.php
@@ -0,0 +1,960 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's sqlite extension
+ * for interacting with SQLite databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Urs Gehrig <urs@circle.ch>
+ * @author Mika Tuupola <tuupola@appelsiini.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
+ * @version CVS: $Id: sqlite.php,v 1.118 2007/11/26 22:57:18 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's sqlite extension
+ * for interacting with SQLite databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * NOTICE: This driver needs PHP's track_errors ini setting to be on.
+ * It is automatically turned on when connecting to the database.
+ * Make sure your scripts don't turn it off.
+ *
+ * @category Database
+ * @package DB
+ * @author Urs Gehrig <urs@circle.ch>
+ * @author Mika Tuupola <tuupola@appelsiini.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_sqlite extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'sqlite';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'sqlite';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ *
+ * {@internal Error codes according to sqlite_exec. See the online
+ * manual at http://sqlite.org/c_interface.html for info.
+ * This error handling based on sqlite_exec is not yet implemented.}}
+ *
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * SQLite data types
+ *
+ * @link http://www.sqlite.org/datatypes.html
+ *
+ * @var array
+ */
+ var $keywords = array (
+ 'BLOB' => '',
+ 'BOOLEAN' => '',
+ 'CHARACTER' => '',
+ 'CLOB' => '',
+ 'FLOAT' => '',
+ 'INTEGER' => '',
+ 'KEY' => '',
+ 'NATIONAL' => '',
+ 'NUMERIC' => '',
+ 'NVARCHAR' => '',
+ 'PRIMARY' => '',
+ 'TEXT' => '',
+ 'TIMESTAMP' => '',
+ 'UNIQUE' => '',
+ 'VARCHAR' => '',
+ 'VARYING' => '',
+ );
+
+ /**
+ * The most recent error message from $php_errormsg
+ * @var string
+ * @access private
+ */
+ var $_lasterror = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_sqlite()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's sqlite driver supports the following extra DSN options:
+ * + mode The permissions for the database file, in four digit
+ * chmod octal format (eg "0600").
+ *
+ * Example of connecting to a database in read-only mode:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400';
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db = DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('sqlite')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ if (!$dsn['database']) {
+ return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+
+ if ($dsn['database'] !== ':memory:') {
+ if (!file_exists($dsn['database'])) {
+ if (!touch($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+ }
+ if (!isset($dsn['mode']) ||
+ !is_numeric($dsn['mode']))
+ {
+ $mode = 0644;
+ } else {
+ $mode = octdec($dsn['mode']);
+ }
+ if (!chmod($dsn['database'], $mode)) {
+ return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+ }
+ if (!file_exists($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+ }
+ }
+ if (!is_file($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_INVALID);
+ }
+ if (!is_readable($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+ }
+
+ $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open';
+
+ // track_errors must remain on for simpleQuery()
+ @ini_set('track_errors', 1);
+ $php_errormsg = '';
+
+ if (!$this->connection = @$connect_function($dsn['database'])) {
+ return $this->raiseError(DB_ERROR_NODBSELECTED,
+ null, null, null,
+ $php_errormsg);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @sqlite_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * NOTICE: This method needs PHP's track_errors ini setting to be on.
+ * It is automatically turned on when connecting to the database.
+ * Make sure your scripts don't turn it off.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+
+ $php_errormsg = '';
+
+ $result = @sqlite_query($query, $this->connection);
+ $this->_lasterror = $php_errormsg ? $php_errormsg : '';
+
+ $this->result = $result;
+ if (!$this->result) {
+ return $this->sqliteRaiseError(null);
+ }
+
+ // sqlite_query() seems to allways return a resource
+ // so cant use that. Using $ismanip instead
+ if (!$ismanip) {
+ $numRows = $this->numRows($result);
+ if (is_object($numRows)) {
+ // we've got PEAR_Error
+ return $numRows;
+ }
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal sqlite result pointer to the next available result
+ *
+ * @param resource $result the valid sqlite result resource
+ *
+ * @return bool true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@sqlite_seek($this->result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @sqlite_fetch_array($result, SQLITE_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+
+ /* Remove extraneous " characters from the fields in the result.
+ * Fixes bug #11716. */
+ if (is_array($arr) && count($arr) > 0) {
+ $strippedArr = array();
+ foreach ($arr as $field => $value) {
+ $strippedArr[trim($field, '"')] = $value;
+ }
+ $arr = $strippedArr;
+ }
+ } else {
+ $arr = @sqlite_fetch_array($result, SQLITE_NUM);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult(&$result)
+ {
+ // XXX No native free?
+ if (!is_resource($result)) {
+ return false;
+ }
+ $result = null;
+ return true;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @sqlite_num_fields($result);
+ if (!$cols) {
+ return $this->sqliteRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @sqlite_num_rows($result);
+ if ($rows === null) {
+ return $this->sqliteRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affected()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return @sqlite_changes($this->connection);
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_sqlite::nextID(), DB_sqlite::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_sqlite::nextID(), DB_sqlite::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $query = 'CREATE TABLE ' . $seqname .
+ ' (id INTEGER UNSIGNED PRIMARY KEY) ';
+ $result = $this->query($query);
+ if (DB::isError($result)) {
+ return($result);
+ }
+ $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname
+ BEGIN
+ DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID();
+ END ";
+ $result = $this->query($query);
+ if (DB::isError($result)) {
+ return($result);
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_sqlite::createSequence(), DB_sqlite::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)");
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ $id = @sqlite_last_insert_rowid($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ $repeat = 1;
+ }
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ // }}}
+ // {{{ getDbFileStats()
+
+ /**
+ * Get the file stats for the current database
+ *
+ * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size,
+ * atime, mtime, ctime, blksize, blocks or a numeric key between
+ * 0 and 12.
+ *
+ * @param string $arg the array key for stats()
+ *
+ * @return mixed an array on an unspecified key, integer on a passed
+ * arg and false at a stats error
+ */
+ function getDbFileStats($arg = '')
+ {
+ $stats = stat($this->dsn['database']);
+ if ($stats == false) {
+ return false;
+ }
+ if (is_array($stats)) {
+ if (is_numeric($arg)) {
+ if (((int)$arg <= 12) & ((int)$arg >= 0)) {
+ return false;
+ }
+ return $stats[$arg ];
+ }
+ if (array_key_exists(trim($arg), $stats)) {
+ return $stats[$arg ];
+ }
+ }
+ return $stats;
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * In SQLite, this makes things safe for inserts/updates, but may
+ * cause problems when performing text comparisons against columns
+ * containing binary data. See the
+ * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @since Method available since Release 1.6.1
+ * @see DB_common::escapeSimple()
+ */
+ function escapeSimple($str)
+ {
+ return @sqlite_escape_string($str);
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return "$query LIMIT $count OFFSET $from";
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * This little hack lets you know how many rows were deleted
+ * when running a "DELETE FROM table" query. Only implemented
+ * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_common::setOption()
+ */
+ function modifyQuery($query)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
+ if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+ $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+ 'DELETE FROM \1 WHERE 1=1', $query);
+ }
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ sqliteRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_sqlite::errorNative(), DB_sqlite::errorCode()
+ */
+ function sqliteRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+
+ $errorcode = @sqlite_last_error($this->connection);
+ $userinfo = "$errorcode ** $this->last_query";
+
+ return $this->raiseError($errno, null, null, $userinfo, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * {@internal This is used to retrieve more meaningfull error messages
+ * because sqlite_last_error() does not provide adequate info.}}
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return $this->_lasterror;
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message
+ *
+ * @param string $errormsg the error message returned from the database
+ *
+ * @return integer the DB error number
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+
+ // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+ // this hack to work around it, per bug #9599.
+ $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg);
+
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/^no such table:/' => DB_ERROR_NOSUCHTABLE,
+ '/^no such index:/' => DB_ERROR_NOT_FOUND,
+ '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS,
+ '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT,
+ '/is not unique/' => DB_ERROR_CONSTRAINT,
+ '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT,
+ '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT,
+ '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/^no such column:/' => DB_ERROR_NOSUCHFIELD,
+ '/no column named/' => DB_ERROR_NOSUCHFIELD,
+ '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD,
+ '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX,
+ '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table
+ *
+ * @param string $result a string containing the name of a table
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @sqlite_array_query($this->connection,
+ "PRAGMA table_info('$result');",
+ SQLITE_ASSOC);
+ $got_string = true;
+ } else {
+ $this->last_query = '';
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null,
+ 'This DBMS can not obtain tableInfo' .
+ ' from result sets');
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = count($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ if (strpos($id[$i]['type'], '(') !== false) {
+ $bits = explode('(', $id[$i]['type']);
+ $type = $bits[0];
+ $len = rtrim($bits[1],')');
+ } else {
+ $type = $id[$i]['type'];
+ $len = 0;
+ }
+
+ $flags = '';
+ if ($id[$i]['pk']) {
+ $flags .= 'primary_key ';
+ }
+ if ($id[$i]['notnull']) {
+ $flags .= 'not_null ';
+ }
+ if ($id[$i]['dflt_value'] !== null) {
+ $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']);
+ }
+ $flags = trim($flags);
+
+ $res[$i] = array(
+ 'table' => $case_func($result),
+ 'name' => $case_func($id[$i]['name']),
+ 'type' => $type,
+ 'len' => $len,
+ 'flags' => $flags,
+ );
+
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ * @param array $args SQLITE DRIVER ONLY: a private array of arguments
+ * used by the getSpecialQuery(). Do not use
+ * this directly.
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type, $args = array())
+ {
+ if (!is_array($args)) {
+ return $this->raiseError('no key specified', null, null, null,
+ 'Argument has to be an array.');
+ }
+
+ switch ($type) {
+ case 'master':
+ return 'SELECT * FROM sqlite_master;';
+ case 'tables':
+ return "SELECT name FROM sqlite_master WHERE type='table' "
+ . 'UNION ALL SELECT name FROM sqlite_temp_master '
+ . "WHERE type='table' ORDER BY name;";
+ case 'schema':
+ return 'SELECT sql FROM (SELECT * FROM sqlite_master '
+ . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+ . "WHERE type!='meta' "
+ . 'ORDER BY tbl_name, type DESC, name;';
+ case 'schemax':
+ case 'schema_x':
+ /*
+ * Use like:
+ * $res = $db->query($db->getSpecialQuery('schema_x',
+ * array('table' => 'table3')));
+ */
+ return 'SELECT sql FROM (SELECT * FROM sqlite_master '
+ . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+ . "WHERE tbl_name LIKE '{$args['table']}' "
+ . "AND type!='meta' "
+ . 'ORDER BY type DESC, name;';
+ case 'alter':
+ /*
+ * SQLite does not support ALTER TABLE; this is a helper query
+ * to handle this. 'table' represents the table name, 'rows'
+ * the news rows to create, 'save' the row(s) to keep _with_
+ * the data.
+ *
+ * Use like:
+ * $args = array(
+ * 'table' => $table,
+ * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT",
+ * 'save' => "NULL, titel, content, datetime"
+ * );
+ * $res = $db->query( $db->getSpecialQuery('alter', $args));
+ */
+ $rows = strtr($args['rows'], $this->keywords);
+
+ $q = array(
+ 'BEGIN TRANSACTION',
+ "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})",
+ "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}",
+ "DROP TABLE {$args['table']}",
+ "CREATE TABLE {$args['table']} ({$args['rows']})",
+ "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup",
+ "DROP TABLE {$args['table']}_backup",
+ 'COMMIT',
+ );
+
+ /*
+ * This is a dirty hack, since the above query will not get
+ * executed with a single query call so here the query method
+ * will be called directly and return a select instead.
+ */
+ foreach ($q as $query) {
+ $this->query($query);
+ }
+ return "SELECT * FROM {$args['table']};";
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/storage.php b/_darcs/pristine/extlib/DB/storage.php
new file mode 100644
index 000000000..ffa2d9447
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/storage.php
@@ -0,0 +1,506 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Provides an object interface to a table row
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <stig@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: storage.php,v 1.24 2007/08/12 05:27:25 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB class so it can be extended from
+ */
+require_once 'DB.php';
+
+/**
+ * Provides an object interface to a table row
+ *
+ * It lets you add, delete and change rows using objects rather than SQL
+ * statements.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <stig@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_storage extends PEAR
+{
+ // {{{ properties
+
+ /** the name of the table (or view, if the backend database supports
+ updates in views) we hold data from */
+ var $_table = null;
+
+ /** which column(s) in the table contains primary keys, can be a
+ string for single-column primary keys, or an array of strings
+ for multiple-column primary keys */
+ var $_keycolumn = null;
+
+ /** DB connection handle used for all transactions */
+ var $_dbh = null;
+
+ /** an assoc with the names of database fields stored as properties
+ in this object */
+ var $_properties = array();
+
+ /** an assoc with the names of the properties in this object that
+ have been changed since they were fetched from the database */
+ var $_changes = array();
+
+ /** flag that decides if data in this object can be changed.
+ objects that don't have their table's key column in their
+ property lists will be flagged as read-only. */
+ var $_readonly = false;
+
+ /** function or method that implements a validator for fields that
+ are set, this validator function returns true if the field is
+ valid, false if not */
+ var $_validator = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Constructor
+ *
+ * @param $table string the name of the database table
+ *
+ * @param $keycolumn mixed string with name of key column, or array of
+ * strings if the table has a primary key of more than one column
+ *
+ * @param $dbh object database connection object
+ *
+ * @param $validator mixed function or method used to validate
+ * each new value, called with three parameters: the name of the
+ * field/column that is changing, a reference to the new value and
+ * a reference to this object
+ *
+ */
+ function DB_storage($table, $keycolumn, &$dbh, $validator = null)
+ {
+ $this->PEAR('DB_Error');
+ $this->_table = $table;
+ $this->_keycolumn = $keycolumn;
+ $this->_dbh = $dbh;
+ $this->_readonly = false;
+ $this->_validator = $validator;
+ }
+
+ // }}}
+ // {{{ _makeWhere()
+
+ /**
+ * Utility method to build a "WHERE" clause to locate ourselves in
+ * the table.
+ *
+ * XXX future improvement: use rowids?
+ *
+ * @access private
+ */
+ function _makeWhere($keyval = null)
+ {
+ if (is_array($this->_keycolumn)) {
+ if ($keyval === null) {
+ for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
+ $keyval[] = $this->{$this->_keycolumn[$i]};
+ }
+ }
+ $whereclause = '';
+ for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
+ if ($i > 0) {
+ $whereclause .= ' AND ';
+ }
+ $whereclause .= $this->_keycolumn[$i];
+ if (is_null($keyval[$i])) {
+ // there's not much point in having a NULL key,
+ // but we support it anyway
+ $whereclause .= ' IS NULL';
+ } else {
+ $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
+ }
+ }
+ } else {
+ if ($keyval === null) {
+ $keyval = @$this->{$this->_keycolumn};
+ }
+ $whereclause = $this->_keycolumn;
+ if (is_null($keyval)) {
+ // there's not much point in having a NULL key,
+ // but we support it anyway
+ $whereclause .= ' IS NULL';
+ } else {
+ $whereclause .= ' = ' . $this->_dbh->quote($keyval);
+ }
+ }
+ return $whereclause;
+ }
+
+ // }}}
+ // {{{ setup()
+
+ /**
+ * Method used to initialize a DB_storage object from the
+ * configured table.
+ *
+ * @param $keyval mixed the key[s] of the row to fetch (string or array)
+ *
+ * @return int DB_OK on success, a DB error if not
+ */
+ function setup($keyval)
+ {
+ $whereclause = $this->_makeWhere($keyval);
+ $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
+ $sth = $this->_dbh->query($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
+ if (DB::isError($row)) {
+ return $row;
+ }
+ if (!$row) {
+ return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null,
+ $query, null, true);
+ }
+ foreach ($row as $key => $value) {
+ $this->_properties[$key] = true;
+ $this->$key = $value;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ insert()
+
+ /**
+ * Create a new (empty) row in the configured table for this
+ * object.
+ */
+ function insert($newpk)
+ {
+ if (is_array($this->_keycolumn)) {
+ $primarykey = $this->_keycolumn;
+ } else {
+ $primarykey = array($this->_keycolumn);
+ }
+ settype($newpk, "array");
+ for ($i = 0; $i < sizeof($primarykey); $i++) {
+ $pkvals[] = $this->_dbh->quote($newpk[$i]);
+ }
+
+ $sth = $this->_dbh->query("INSERT INTO $this->_table (" .
+ implode(",", $primarykey) . ") VALUES(" .
+ implode(",", $pkvals) . ")");
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ if (sizeof($newpk) == 1) {
+ $newpk = $newpk[0];
+ }
+ $this->setup($newpk);
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Output a simple description of this DB_storage object.
+ * @return string object description
+ */
+ function toString()
+ {
+ $info = strtolower(get_class($this));
+ $info .= " (table=";
+ $info .= $this->_table;
+ $info .= ", keycolumn=";
+ if (is_array($this->_keycolumn)) {
+ $info .= "(" . implode(",", $this->_keycolumn) . ")";
+ } else {
+ $info .= $this->_keycolumn;
+ }
+ $info .= ", dbh=";
+ if (is_object($this->_dbh)) {
+ $info .= $this->_dbh->toString();
+ } else {
+ $info .= "null";
+ }
+ $info .= ")";
+ if (sizeof($this->_properties)) {
+ $info .= " [loaded, key=";
+ $keyname = $this->_keycolumn;
+ if (is_array($keyname)) {
+ $info .= "(";
+ for ($i = 0; $i < sizeof($keyname); $i++) {
+ if ($i > 0) {
+ $info .= ",";
+ }
+ $info .= $this->$keyname[$i];
+ }
+ $info .= ")";
+ } else {
+ $info .= $this->$keyname;
+ }
+ $info .= "]";
+ }
+ if (sizeof($this->_changes)) {
+ $info .= " [modified]";
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ dump()
+
+ /**
+ * Dump the contents of this object to "standard output".
+ */
+ function dump()
+ {
+ foreach ($this->_properties as $prop => $foo) {
+ print "$prop = ";
+ print htmlentities($this->$prop);
+ print "<br />\n";
+ }
+ }
+
+ // }}}
+ // {{{ &create()
+
+ /**
+ * Static method used to create new DB storage objects.
+ * @param $data assoc. array where the keys are the names
+ * of properties/columns
+ * @return object a new instance of DB_storage or a subclass of it
+ */
+ function &create($table, &$data)
+ {
+ $classname = strtolower(get_class($this));
+ $obj = new $classname($table);
+ foreach ($data as $name => $value) {
+ $obj->_properties[$name] = true;
+ $obj->$name = &$value;
+ }
+ return $obj;
+ }
+
+ // }}}
+ // {{{ loadFromQuery()
+
+ /**
+ * Loads data into this object from the given query. If this
+ * object already contains table data, changes will be saved and
+ * the object re-initialized first.
+ *
+ * @param $query SQL query
+ *
+ * @param $params parameter list in case you want to use
+ * prepare/execute mode
+ *
+ * @return int DB_OK on success, DB_WARNING_READ_ONLY if the
+ * returned object is read-only (because the object's specified
+ * key column was not found among the columns returned by $query),
+ * or another DB error code in case of errors.
+ */
+// XXX commented out for now
+/*
+ function loadFromQuery($query, $params = null)
+ {
+ if (sizeof($this->_properties)) {
+ if (sizeof($this->_changes)) {
+ $this->store();
+ $this->_changes = array();
+ }
+ $this->_properties = array();
+ }
+ $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
+ if (DB::isError($rowdata)) {
+ return $rowdata;
+ }
+ reset($rowdata);
+ $found_keycolumn = false;
+ while (list($key, $value) = each($rowdata)) {
+ if ($key == $this->_keycolumn) {
+ $found_keycolumn = true;
+ }
+ $this->_properties[$key] = true;
+ $this->$key = &$value;
+ unset($value); // have to unset, or all properties will
+ // refer to the same value
+ }
+ if (!$found_keycolumn) {
+ $this->_readonly = true;
+ return DB_WARNING_READ_ONLY;
+ }
+ return DB_OK;
+ }
+ */
+
+ // }}}
+ // {{{ set()
+
+ /**
+ * Modify an attriute value.
+ */
+ function set($property, $newvalue)
+ {
+ // only change if $property is known and object is not
+ // read-only
+ if ($this->_readonly) {
+ return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
+ null, null, null, true);
+ }
+ if (@isset($this->_properties[$property])) {
+ if (empty($this->_validator)) {
+ $valid = true;
+ } else {
+ $valid = @call_user_func($this->_validator,
+ $this->_table,
+ $property,
+ $newvalue,
+ $this->$property,
+ $this);
+ }
+ if ($valid) {
+ $this->$property = $newvalue;
+ if (empty($this->_changes[$property])) {
+ $this->_changes[$property] = 0;
+ } else {
+ $this->_changes[$property]++;
+ }
+ } else {
+ return $this->raiseError(null, DB_ERROR_INVALID, null,
+ null, "invalid field: $property",
+ null, true);
+ }
+ return true;
+ }
+ return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null,
+ null, "unknown field: $property",
+ null, true);
+ }
+
+ // }}}
+ // {{{ &get()
+
+ /**
+ * Fetch an attribute value.
+ *
+ * @param string attribute name
+ *
+ * @return attribute contents, or null if the attribute name is
+ * unknown
+ */
+ function &get($property)
+ {
+ // only return if $property is known
+ if (isset($this->_properties[$property])) {
+ return $this->$property;
+ }
+ $tmp = null;
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ _DB_storage()
+
+ /**
+ * Destructor, calls DB_storage::store() if there are changes
+ * that are to be kept.
+ */
+ function _DB_storage()
+ {
+ if (sizeof($this->_changes)) {
+ $this->store();
+ }
+ $this->_properties = array();
+ $this->_changes = array();
+ $this->_table = null;
+ }
+
+ // }}}
+ // {{{ store()
+
+ /**
+ * Stores changes to this object in the database.
+ *
+ * @return DB_OK or a DB error
+ */
+ function store()
+ {
+ $params = array();
+ $vars = array();
+ foreach ($this->_changes as $name => $foo) {
+ $params[] = &$this->$name;
+ $vars[] = $name . ' = ?';
+ }
+ if ($vars) {
+ $query = 'UPDATE ' . $this->_table . ' SET ' .
+ implode(', ', $vars) . ' WHERE ' .
+ $this->_makeWhere();
+ $stmt = $this->_dbh->prepare($query);
+ $res = $this->_dbh->execute($stmt, $params);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ $this->_changes = array();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove the row represented by this object from the database.
+ *
+ * @return mixed DB_OK or a DB error
+ */
+ function remove()
+ {
+ if ($this->_readonly) {
+ return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
+ null, null, null, true);
+ }
+ $query = 'DELETE FROM ' . $this->_table .' WHERE '.
+ $this->_makeWhere();
+ $res = $this->_dbh->query($query);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ foreach ($this->_properties as $prop => $foo) {
+ unset($this->$prop);
+ }
+ $this->_properties = array();
+ $this->_changes = array();
+ return DB_OK;
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/_darcs/pristine/extlib/DB/sybase.php b/_darcs/pristine/extlib/DB/sybase.php
new file mode 100644
index 000000000..3befbf6ea
--- /dev/null
+++ b/_darcs/pristine/extlib/DB/sybase.php
@@ -0,0 +1,942 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's sybase extension
+ * for interacting with Sybase databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Antônio Carlos Venâncio Júnior <floripa@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: sybase.php,v 1.87 2007/09/21 13:40:42 aharvey Exp $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's sybase extension
+ * for interacting with Sybase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * WARNING: This driver may fail with multiple connections under the
+ * same user/pass/host and different databases.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Antônio Carlos Venâncio Júnior <floripa@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2007 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.7.14RC1
+ * @link http://pear.php.net/package/DB
+ */
+class DB_sybase extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'sybase';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'sybase';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_sybase()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's sybase driver supports the following extra DSN options:
+ * + appname The application name to use on this connection.
+ * Available since PEAR DB 1.7.0.
+ * + charset The character set to use on this connection.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('sybase') &&
+ !PEAR::loadExtension('sybase_ct'))
+ {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost';
+ $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false;
+ $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false;
+ $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false;
+
+ $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect';
+
+ if ($dsn['username']) {
+ $this->connection = @$connect_function($dsn['hostspec'],
+ $dsn['username'],
+ $dsn['password'],
+ $dsn['charset'],
+ $dsn['appname']);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ 'The DSN did not contain a username.');
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ @sybase_get_last_message());
+ }
+
+ if ($dsn['database']) {
+ if (!@sybase_select_db($dsn['database'], $this->connection)) {
+ return $this->raiseError(DB_ERROR_NODBSELECTED,
+ null, null, null,
+ @sybase_get_last_message());
+ }
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @sybase_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = $this->_checkManip($query);
+ $this->last_query = $query;
+ if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @sybase_query('BEGIN TRANSACTION', $this->connection);
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @sybase_query($query, $this->connection);
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ if (is_resource($result)) {
+ return $result;
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ return $ismanip ? DB_OK : $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal sybase result pointer to the next available result
+ *
+ * @param a valid sybase result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@sybase_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ if (function_exists('sybase_fetch_assoc')) {
+ $arr = @sybase_fetch_assoc($result);
+ } else {
+ if ($arr = @sybase_fetch_array($result)) {
+ foreach ($arr as $key => $value) {
+ if (is_int($key)) {
+ unset($arr[$key]);
+ }
+ }
+ }
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @sybase_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return is_resource($result) ? sybase_free_result($result) : false;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @sybase_num_fields($result);
+ if (!$cols) {
+ return $this->sybaseRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @sybase_num_rows($result);
+ if ($rows === false) {
+ return $this->sybaseRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->_last_query_manip) {
+ $result = @sybase_affected_rows($this->connection);
+ } else {
+ $result = 0;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_sybase::createSequence(), DB_sybase::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
+ {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } elseif (!DB::isError($result)) {
+ $result = $this->query("SELECT @@IDENTITY FROM $seqname");
+ $repeat = 0;
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ return $result[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_sybase::nextID(), DB_sybase::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE TABLE '
+ . $this->getSequenceName($seq_name)
+ . ' (id numeric(10, 0) IDENTITY NOT NULL,'
+ . ' vapor int NULL)');
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_sybase::nextID(), DB_sybase::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ quoteFloat()
+
+ /**
+ * Formats a float value for use within a query in a locale-independent
+ * manner.
+ *
+ * @param float the float value to be quoted.
+ * @return string the quoted string.
+ * @see DB_common::quoteSmart()
+ * @since Method available since release 1.7.8.
+ */
+ function quoteFloat($float) {
+ return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @sybase_query('COMMIT', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @sybase_query('ROLLBACK', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ sybaseRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_sybase::errorNative(), DB_sybase::errorCode()
+ */
+ function sybaseRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @sybase_get_last_message();
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message.
+ *
+ * @param string $errormsg error message returned from the database
+ * @return integer an error number from a DB error constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+
+ // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+ // this hack to work around it, per bug #9599.
+ $errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg);
+
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/Incorrect syntax near/'
+ => DB_ERROR_SYNTAX,
+ '/^Unclosed quote before the character string [\"\'].*[\"\']\./'
+ => DB_ERROR_SYNTAX,
+ '/Implicit conversion (from datatype|of NUMERIC value)/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./'
+ => DB_ERROR_NOSUCHTABLE,
+ '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^.+ permission denied on object .+, database .+, owner .+/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^.* permission denied, database .+, owner .+/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/[^.*] not found\./'
+ => DB_ERROR_NOSUCHTABLE,
+ '/There is already an object named/'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/Invalid column name/'
+ => DB_ERROR_NOSUCHFIELD,
+ '/does not allow null values/'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/Command has been aborted/'
+ => DB_ERROR_CONSTRAINT,
+ '/^Cannot drop the index .* because it doesn\'t exist/i'
+ => DB_ERROR_NOT_FOUND,
+ '/^There is already an index/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/^There are fewer columns in the INSERT statement than values specified/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '/Divide by zero/i'
+ => DB_ERROR_DIVZERO,
+ );
+ }
+
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.6.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $id = @sybase_query("SELECT * FROM $result WHERE 1=0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @sybase_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $f = @sybase_fetch_field($id, $i);
+ // column_source is often blank
+ $res[$i] = array(
+ 'table' => $got_string
+ ? $case_func($result)
+ : $case_func($f->column_source),
+ 'name' => $case_func($f->name),
+ 'type' => $f->type,
+ 'len' => $f->max_length,
+ 'flags' => '',
+ );
+ if ($res[$i]['table']) {
+ $res[$i]['flags'] = $this->_sybase_field_flags(
+ $res[$i]['table'], $res[$i]['name']);
+ }
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @sybase_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _sybase_field_flags()
+
+ /**
+ * Get the flags for a field
+ *
+ * Currently supports:
+ * + <samp>unique_key</samp> (unique index, unique check or primary_key)
+ * + <samp>multiple_key</samp> (multi-key index)
+ *
+ * @param string $table the table name
+ * @param string $column the field name
+ *
+ * @return string space delimited string of flags. Empty string if none.
+ *
+ * @access private
+ */
+ function _sybase_field_flags($table, $column)
+ {
+ static $tableName = null;
+ static $flags = array();
+
+ if ($table != $tableName) {
+ $flags = array();
+ $tableName = $table;
+
+ /* We're running sp_helpindex directly because it doesn't exist in
+ * older versions of ASE -- unfortunately, we can't just use
+ * DB::isError() because the user may be using callback error
+ * handling. */
+ $res = @sybase_query("sp_helpindex $table", $this->connection);
+
+ if ($res === false || $res === true) {
+ // Fake a valid response for BC reasons.
+ return '';
+ }
+
+ while (($val = sybase_fetch_assoc($res)) !== false) {
+ if (!isset($val['index_keys'])) {
+ /* No useful information returned. Break and be done with
+ * it, which preserves the pre-1.7.9 behaviour. */
+ break;
+ }
+
+ $keys = explode(', ', trim($val['index_keys']));
+
+ if (sizeof($keys) > 1) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'multiple_key');
+ }
+ }
+
+ if (strpos($val['index_description'], 'unique')) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'unique_key');
+ }
+ }
+ }
+
+ sybase_free_result($res);
+
+ }
+
+ if (array_key_exists($column, $flags)) {
+ return(implode(' ', $flags[$column]));
+ }
+
+ return '';
+ }
+
+ // }}}
+ // {{{ _add_flag()
+
+ /**
+ * Adds a string to the flags array if the flag is not yet in there
+ * - if there is no flag present the array is created
+ *
+ * @param array $array reference of flags array to add a value to
+ * @param mixed $value value to add to the flag array
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _add_flag(&$array, $value)
+ {
+ if (!is_array($array)) {
+ $array = array($value);
+ } elseif (!in_array($value, $array)) {
+ array_push($array, $value);
+ }
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return "SELECT name FROM sysobjects WHERE type = 'U'"
+ . ' ORDER BY name';
+ case 'views':
+ return "SELECT name FROM sysobjects WHERE type = 'V'";
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>