]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Ugly patch to maintain old DB handle code working quietly
authorDiogo Cordeiro <diogo@fc.up.pt>
Sat, 27 Apr 2019 17:21:14 +0000 (18:21 +0100)
committerDiogo Cordeiro <diogo@fc.up.pt>
Sat, 27 Apr 2019 17:23:51 +0000 (18:23 +0100)
We have to replace this database engine with a modern one

21 files changed:
extlib/DB/DataObject.php
extlib/DB/DataObject/Cast.php
extlib/DB/DataObject/Error.php
extlib/DB/DataObject/Generator.php
extlib/DB/DataObject/Links.php
extlib/DB/DataObject/createTables.php
extlib/DB/common.php
extlib/DB/dbase.php
extlib/DB/fbsql.php
extlib/DB/ibase.php
extlib/DB/ifx.php
extlib/DB/msql.php
extlib/DB/mssql.php
extlib/DB/mysql.php
extlib/DB/mysqli.php
extlib/DB/oci8.php
extlib/DB/odbc.php
extlib/DB/pgsql.php
extlib/DB/sqlite.php
extlib/DB/storage.php
extlib/DB/sybase.php

index e31f35db9f3bb6ef5e62f482a93d6cb846a8dcd0..aa569325a0d641dd89c4995acb79e2da1e5a0a26 100644 (file)
@@ -18,7 +18,7 @@
  * @version    CVS: $Id: DataObject.php 336751 2015-05-12 04:39:50Z alan_k $
  * @link       http://pear.php.net/package/DB_DataObject
  */
-  
+
 
 /* ===========================================================================
  *
  *  ===========================================================================
  */
 
-/**
- * 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)
@@ -98,9 +45,6 @@ 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.
@@ -160,7 +104,7 @@ define('DB_DATAOBJECT_WHEREADD_ONLY', true);
  *   - 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']['RESULTS'] = array();
 $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ'] = 1;
 $GLOBALS['_DB_DATAOBJECT']['RESULTFIELDS'] = array();
 $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'] = array();
@@ -174,11 +118,10 @@ $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = false;
 $GLOBALS['_DB_DATAOBJECT']['QUERYENDTIME'] = 0;
 
 
 // this will be horrifically slow!!!!
 // these two are BC/FC handlers for call in PHP4/5
 
+
 if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
     class DB_DataObject_Overload
     {
@@ -188,9 +131,10 @@ if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
             $this->_call($method, $args, $return);
             return $return;
         }
+
         public function __sleep()
         {
-            return array_keys(get_object_vars($this)) ;
+            return array_keys(get_object_vars($this));
         }
     }
 } else {
@@ -200,18 +144,13 @@ if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
 }
 
 
-    
-
-
+/*
+*
+* @package  DB_DataObject
+* @author   Alan Knowles <alan@akbkhome.com>
+* @since    PHP 4.0
+*/
 
- /*
- *
- * @package  DB_DataObject
- * @author   Alan Knowles <alan@akbkhome.com>
- * @since    PHP 4.0
- */
 class DB_DataObject extends DB_DataObject_Overload
 {
     /**
@@ -242,1562 +181,1074 @@ class DB_DataObject extends DB_DataObject_Overload
     /*                      Major Public Methods                     */
     /* (designed to be optionally then called with parent::method()) */
     /* ============================================================= */
-
-
     /**
-     * Get a result using key, value.
+     * 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..
      *
-     * 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
+     * @access  private
+     * @var     string
+     */
+    public $_database_dsn = '';
+    /**
+     * The Database connection id (md5 sum of databasedsn)
      *
-     * see the fetch example on how to extend this.
+     * @access  private
+     * @var     string
+     */
+    public $_database_dsn_md5 = '';
+    /**
+     * The Database name
+     * created in __connection
      *
-     * 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.
+     * @access  private
+     * @var  string
+     */
+    public $_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
+     */
+    public $_query = array(
+        'condition' => '', // the WHERE condition
+        'group_by' => '', // the GROUP BY condition
+        'order_by' => '', // the ORDER BY condition
+        'having' => '', // the HAVING condition
+        'useindex' => '', // the USE INDEX condition
+        'limit_start' => '', // the LIMIT condition
+        'limit_count' => '', // the LIMIT condition
+        'data_select' => '*', // the columns to be SELECTed
+        'unions' => array(), // the added unions,
+        'derive_table' => '', // derived table name (BETA)
+        'derive_select' => '', // derived table select (BETA)
+    );
+    /**
+     * Database result id (references global $_DB_DataObject[results]
+     *
+     * @access  private
+     * @var     integer
+     */
+    public $_DB_resultid;
+    /**
+     * ResultFields - on the last call to fetch(), resultfields is sent here,
+     * so we can clean up the memory.
      *
-     * @param   string  $k column
-     * @param   string  $v value
      * @access  public
-     * @return  int     No. of rows
+     * @var     array
      */
-    public 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->tableName()}", 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);
-    }
-    
+    public $_resultFields = false;
     /**
-     * Get the value of the primary id
+     * Have the links been loaded?
+     * if they have it contains a array of those variables.
      *
-     * While I normally use 'id' as the PRIMARY KEY value, some database use
-     * {table}_id as the column name.
+     * @access  private
+     * @var     boolean | array
+     */
+    public $_link_loaded = false;
+    /**
+     * The JOIN condition
      *
-     * To save a bit of typing,
+     * @access  private
+     * @var     string
+     */
+    public $_join = '';
+    /**
+     * Last Error that has occured
+     * - use $this->_lastError or
+     * $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
      *
-     * $id = $do->pid();
+     * @access  public
+     * @var     object PEAR_Error (or false)
+     */
+    public $_lastError = false;
+
+    /**
+     * sets and returns debug level
+     * eg. DB_DataObject::debugLevel(4);
      *
-     * @return the id
+     * @param int $v level
+     * @access  public
+     * @return int|none
      */
-    public function pid()
+    public static function debugLevel($v = null)
     {
-        $keys = $this->keys();
-        if (!$keys) {
-            $this->raiseError(
-                "No Keys available for {$this->tableName()}",
-                DB_DATAOBJECT_ERROR_INVALIDCONFIG
-            );
-            return false;
+        global $_DB_DATAOBJECT;
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            (new DB_DataObject)->_loadConfig();
         }
-        $k = $keys[0];
-        if (empty($this->$k)) { // we do not
-            $this->raiseError(
-                "pid() called on Object where primary key value not available",
-                DB_DATAOBJECT_ERROR_NODATA
-            );
-            return false;
+        if ($v !== null) {
+            $r = isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
+            $_DB_DATAOBJECT['CONFIG']['debug'] = $v;
+            return $r;
         }
-        return $this->$k;
+        return isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
     }
-    
-
 
     /**
-     * build the basic select query.
+     * Define the global $_DB_DATAOBJECT['CONFIG'] as an alias to  PEAR::getStaticProperty('DB_DataObject','options');
      *
-     * @access private
+     * 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 void an error object
      */
-    
-    public function _build_select()
+    public function _loadConfig()
     {
         global $_DB_DATAOBJECT;
-        $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
-        if ($quoteIdentifiers) {
-            $this->_connect();
-            $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
-        }
-        $tn = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName()) ;
-        if (!empty($this->_query['derive_table']) && !empty($this->_query['derive_select'])) {
-            
-            // this is a derived select..
-            // not much support in the api yet..
-            
-            $sql = 'SELECT ' .
-               $this->_query['derive_select']
-               .' FROM ( SELECT'.
-                    $this->_query['data_select'] . " \n" .
-                    " FROM   $tn  " . $this->_query['useindex'] . " \n" .
-                    $this->_join . " \n" .
-                    $this->_query['condition'] . " \n" .
-                    $this->_query['group_by'] . " \n" .
-                    $this->_query['having'] . " \n" .
-                ') ' . $this->_query['derive_table'];
-                     
-            return $sql;
-        }
-        
-       
-        
-        $sql = 'SELECT ' .
-            $this->_query['data_select'] . " \n" .
-            " FROM   $tn  " . $this->_query['useindex'] . " \n" .
-            $this->_join . " \n" .
-            $this->_query['condition'] . " \n" .
-            $this->_query['group_by'] . " \n" .
-            $this->_query['having'] . " \n";
-                 
-        return $sql;
+
+        $_DB_DATAOBJECT['CONFIG'] = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
+        return null;
     }
 
-     
     /**
-     * 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
-     *
-     * if an error occurs $object->N will be set to false and return value will also be false;
-     * if numRows is not supported it will
-     *
-     *
-     * @param   boolean $n Fetch first result
-     * @access  public
-     * @return  mixed (number of rows returned, or true if numRows fetching is not supported)
+     * (deprecated - use ::get / and your own caching method)
+     * @param $class
+     * @param $k
+     * @param null $v
+     * @return bool
      */
-    public function find($n = false)
+    public static function staticGet($class, $k, $v = null)
     {
+        $lclass = strtolower($class);
         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();
+            (new DB_DataObject)->_loadConfig();
         }
 
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug($n, "find", 1);
+
+        $key = "$k:$v";
+        if ($v === null) {
+            $key = $k;
         }
-        if (!strlen($this->tableName())) {
-            // xdebug can backtrace this!
-            trigger_error("NO \$__table SPECIFIED in class definition", E_USER_ERROR);
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            (new DB_DataObject)->debug("$class $key", "STATIC GET - TRY CACHE");
         }
-        $this->N = 0;
-        $query_before = $this->_query;
-        $this->_build_condition($this->table()) ;
-        
-       
-        $this->_connect();
-        $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
-       
-        
-        $sql = $this->_build_select();
-        
-        foreach ($this->_query['unions'] as $union_ar) {
-            $sql .=   $union_ar[1] .   $union_ar[0]->_build_select() . " \n";
+        if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) {
+            return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
         }
-        
-        $sql .=  $this->_query['order_by']  . " \n";
-        
-        
-        /* We are checking for method modifyLimitQuery as it is PEAR DB specific */
-        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']);
-            }
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            (new DB_DataObject)->debug("$class $key", "STATIC GET - NOT IN CACHE");
         }
-        
-        
-        $err = $this->_query($sql);
-        if (is_a($err, 'PEAR_Error')) {
-            return false;
-        }
-        
-        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]);
+
+        $obj = DB_DataObject::factory(substr($class, strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
+        if ((new PEAR)->isError($obj)) {
+            $dor = new DB_DataObject();
+            $dor->raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
+            $r = false;
+            return $r;
         }
-        
-        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 (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) {
+            $_DB_DATAOBJECT['CACHE'][$lclass] = array();
         }
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug("DONE", "find", 1);
+        if (!$obj->get($k, $v)) {
+            $dor = new DB_DataObject();
+            $dor->raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
+
+            $r = false;
+            return $r;
         }
-        $this->_query = $query_before;
-        return $ret;
+        $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
+        return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
     }
 
     /**
-     * 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.
-     * }
+     * Debugger. - use this in your extended classes to output debugging information.
      *
-     * to add features to a fetch
-     * function fetch () {
-     *    $ret = parent::fetch();
-     *    $this->date_formated = date('dmY',$this->date);
-     *    return $ret;
-     * }
+     * Uses DB_DataObject::DebugLevel(x) to turn it on
      *
-     * @access  public
-     * @return  boolean on success
+     * @param string $message - message to output
+     * @param int $logtype - bold at start
+     * @param int $level - output level
+     * @return void
+     * @access   public
      */
-    public function fetch()
+    public function debug($message, $logtype = 0, $level = 1)
     {
         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));
-        }
-        $replace = array('.', ' ');
-        foreach ($array as $k=>$v) {
-            // use strpos as str_replace is slow.
-            $kk =  (strpos($k, '.') === false && strpos($k, ' ') === false) ?
-                $k : str_replace($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']) ||
+            (is_numeric($_DB_DATAOBJECT['CONFIG']['debug']) && $_DB_DATAOBJECT['CONFIG']['debug'] < $level)) {
+            return null;
         }
-        
-        // set link flag
-        $this->_link_loaded=false;
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug("{$this->tableName()} DONE", "fetchrow", 2);
+        // 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 (($this->_query !== false) &&  empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) {
-            $this->_query = false;
+        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);
         }
-        return true;
-    }
 
-    
-    /**
-    * fetches all results as an array,
-    *
-    * return format is dependant on args.
-    * if selectAdd() has not been called on the object, then it will add the correct columns to the query.
-    *
-    * A) Array of values (eg. a list of 'id')
-    *
-    * $x = DB_DataObject::factory('mytable');
-    * $x->whereAdd('something = 1')
-    * $ar = $x->fetchAll('id');
-    * -- returns array(1,2,3,4,5)
-    *
-    * B) Array of values (not from table)
-    *
-    * $x = DB_DataObject::factory('mytable');
-    * $x->whereAdd('something = 1');
-    * $x->selectAdd();
-    * $x->selectAdd('distinct(group_id) as group_id');
-    * $ar = $x->fetchAll('group_id');
-    * -- returns array(1,2,3,4,5)
-    *     *
-    * C) A key=>value associative array
-    *
-    * $x = DB_DataObject::factory('mytable');
-    * $x->whereAdd('something = 1')
-    * $ar = $x->fetchAll('id','name');
-    * -- returns array(1=>'fred',2=>'blogs',3=> .......
-    *
-    * D) array of objects
-    * $x = DB_DataObject::factory('mytable');
-    * $x->whereAdd('something = 1');
-    * $ar = $x->fetchAll();
-    *
-    * E) array of arrays (for example)
-    * $x = DB_DataObject::factory('mytable');
-    * $x->whereAdd('something = 1');
-    * $ar = $x->fetchAll(false,false,'toArray');
-    *
-    *
-    * @param    string|false  $k key
-    * @param    string|false  $v value
-    * @param    string|false  $method method to call on each result to get array value (eg. 'toArray')
-    * @access  public
-    * @return  array  format dependant on arguments, may be empty
-    */
-    public function fetchAll($k= false, $v = false, $method = false)
-    {
-        // should it even do this!!!?!?
-        if ($k !== false &&
-                (   // only do this is we have not been explicit..
-                    empty($this->_query['data_select']) ||
-                    ($this->_query['data_select'] == '*')
-                )
-            ) {
-            $this->selectAdd();
-            $this->selectAdd($k);
-            if ($v !== false) {
-                $this->selectAdd($v);
-            }
+        if (!ini_get('html_errors')) {
+            echo "$class   : $logtype       : $message\n";
+            flush();
+            return null;
         }
-        
-        $this->find();
-        $ret = array();
-        while ($this->fetch()) {
-            if ($v !== false) {
-                $ret[$this->$k] = $this->$v;
-                continue;
-            }
-            $ret[] = $k === false ?
-                ($method == false ? clone($this)  : $this->$method())
-                : $this->$k;
+        if (!is_string($message)) {
+            $message = print_r($message, true);
         }
-        return $ret;
+        $colorize = ($logtype == 'ERROR') ? '<font color="red">' : '<font>';
+        echo "<code>{$colorize}<B>$class: $logtype:</B> " . nl2br(htmlspecialchars($message)) . "</font></code><BR>\n";
     }
-    
-    
+
     /**
-     * Adds a condition to the WHERE statement, defaults to AND
+     * 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')
      *
-     * $object->whereAdd(); //reset or cleaer ewhwer
-     * $object->whereAdd("ID > 20");
-     * $object->whereAdd("age > 20","OR");
+     * table name can bedatabasename/table
+     * - and allow modular dataobjects to be written..
+     * (this also helps proxy creation)
      *
-     * @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
+     * Experimental Support for Multi-Database factory eg. mydatabase.mytable
+     *
+     *
+     * @param string $table tablename (use blank to create a new instance of the same class.)
+     * @access private
+     * @return DataObject|PEAR|PEAR_Error|true
      */
-    public function whereAdd($cond = false, $logic = 'AND')
+
+
+    public static function factory($table = '')
     {
-        // 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);
+        global $_DB_DATAOBJECT;
+
+
+        // multi-database support.. - experimental.
+        $database = '';
+
+        if (strpos($table, '/') !== false) {
+            list($database, $table) = explode('.', $table, 2);
         }
-        // check input...= 0 or '   ' == error!
-        if (!trim($cond)) {
-            return $this->raiseError("WhereAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            (new DB_DataObject)->_loadConfig();
         }
-        $r = $_query['condition'];
-        if ($_query['condition']) {
-            $_query['condition'] .= " {$logic} ( {$cond} )";
-            $this->_query = $_query;
-            return $r;
+        // no configuration available for database
+        if (!empty($database) && empty($_DB_DATAOBJECT['CONFIG']['database_' . $database])) {
+            $do = new DB_DataObject();
+            $do->raiseError(
+                "unable to find database_{$database} in Configuration, It is required for factory with database",
+                0,
+                PEAR_ERROR_DIE
+            );
         }
-        $_query['condition'] = " WHERE ( {$cond} ) ";
-        $this->_query = $_query;
-        return $r;
-    }
 
-    /**
-    * Adds a 'IN' condition to the WHERE statement
-    *
-    * $object->whereAddIn('id', $array, 'int'); //minimal usage
-    * $object->whereAddIn('price', $array, 'float', 'OR');  // cast to float, and call whereAdd with 'OR'
-    * $object->whereAddIn('name', $array, 'string');  // quote strings
-    *
-    * @param    string  $key  key column to match
-    * @param    array  $list  list of values to match
-    * @param    string  $type  string|int|integer|float|bool  cast to type.
-    * @param    string  $logic optional logic to call whereAdd with eg. "OR" (defaults to "AND")
-    * @access   public
-    * @return   string|PEAR::Error - previous condition or Error when invalid args found
-    */
-    public function whereAddIn($key, $list, $type, $logic = 'AND')
-    {
-        $not = '';
-        if ($key[0] == '!') {
-            $not = 'NOT ';
-            $key = substr($key, 1);
-        }
-        // fix type for short entry.
-        $type = $type == 'int' ? 'integer' : $type;
 
-        if ($type == 'string') {
-            $this->_connect();
+        /*
+        if ($table === '') {
+            if (is_a($this,'DB_DataObject') && strlen($this->tableName())) {
+                $table = $this->tableName();
+            } else {
+                return DB_DataObject::raiseError(
+                    "factory did not recieve a table name",
+                    DB_DATAOBJECT_ERROR_INVALIDARGS);
+            }
         }
 
-        $ar = array();
-        foreach ($list as $k) {
-            settype($k, $type);
-            $ar[] = $type == 'string' ? $this->_quote($k) : $k;
-        }
-      
-        if (!$ar) {
-            return $not ? $this->_query['condition'] : $this->whereAdd("1=0");
-        }
-        return $this->whereAdd("$key $not IN (". implode(',', $ar). ')', $logic);
-    }
+        */
+        // does this need multi db support??
+        $cp = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
+            explode(PATH_SEPARATOR, $_DB_DATAOBJECT['CONFIG']['class_prefix']) : '';
 
-    
-    
-    /**
-     * 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
-     */
-    public 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;
+        //print_r($cp);
+
+        // multiprefix support.
+        $tbl = preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table));
+        if (is_array($cp)) {
+            $class = array();
+            foreach ($cp as $cpr) {
+                $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($cpr . $tbl, false) : class_exists($cpr . $tbl);
+                if ($ce) {
+                    $class = $cpr . $tbl;
+                    break;
+                }
+                $class[] = $cpr . $tbl;
+            }
+        } else {
+            $class = $tbl;
+            $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($class, false) : class_exists($class);
         }
-        if ($order === false) {
-            $this->_query['order_by'] = '';
-            return;
+
+
+        $rclass = $ce ? $class : (new DB_DataObject)->_autoloadClass($class, $table);
+        // proxy = full|light
+        if (!$rclass && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
+            (new DB_DataObject)->debug("FAILED TO Autoload  $database.$table - using proxy.", "FACTORY", 1);
+
+
+            $proxyMethod = 'getProxy' . $_DB_DATAOBJECT['CONFIG']['proxy'];
+            // if you have loaded (some other way) - dont try and load it again..
+            class_exists('DB_DataObject_Generator') ? '' :
+                //require_once 'DB/DataObject/Generator.php';
+                require_once 'Generator.php';
+
+            $d = new DB_DataObject;
+
+            $d->__table = $table;
+
+            $ret = $d->_connect();
+            if (is_object($ret) && is_a($ret, 'PEAR_Error')) {
+                return $ret;
+            }
+
+            $x = new DB_DataObject_Generator;
+            return $x->$proxyMethod($d->_database, $table);
         }
-        // check input...= 0 or '    ' == error!
-        if (!trim($order)) {
-            return $this->raiseError("orderBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+
+        if (!$rclass || !class_exists($rclass)) {
+            $dor = new DB_DataObject();
+            return $dor->raiseError(
+                "factory could not find class " .
+                (is_array($class) ? implode(PATH_SEPARATOR, $class) : $class) .
+                "from $table",
+                DB_DATAOBJECT_ERROR_INVALIDCONFIG
+            );
         }
-        
-        if (!$this->_query['order_by']) {
-            $this->_query['order_by'] = " ORDER BY {$order} ";
-            return;
+
+        $ret = new $rclass();
+
+        if (!empty($database)) {
+            (new DB_DataObject)->debug("Setting database to $database", "FACTORY", 1);
+            $ret->database($database);
         }
-        $this->_query['order_by'] .= " , {$order}";
+        return $ret;
     }
 
     /**
-     * Adds a group by condition
-     *
-     * $object->groupBy(); //reset the grouping
-     * $object->groupBy("ID DESC");
-     * $object->groupBy("ID,age");
+     * 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  string  $group  Grouping
+     * @param int $message message
+     * @param int $type type
+     * @param int $behaviour behaviour (die or continue!);
      * @access public
-     * @return none|PEAR::Error - invalid args only
+     * @return error|int|object
      */
-    public function groupBy($group = false)
+    public function raiseError($message, $type = null, $behaviour = 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 ($group === false) {
-            $this->_query['group_by'] = '';
-            return;
+        global $_DB_DATAOBJECT;
+
+        if ($behaviour == PEAR_ERROR_DIE && !empty($_DB_DATAOBJECT['CONFIG']['dont_die'])) {
+            $behaviour = null;
         }
-        // check input...= 0 or '    ' == error!
-        if (!trim($group)) {
-            return $this->raiseError("groupBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+
+        $error = &(new PEAR)->getStaticProperty('DB_DataObject', 'lastError');
+
+
+        // no checks for production here?....... - we log  errors before we throw them.
+        DB_DataObject::debug($message, 'ERROR', 1);
+
+
+        if ((new PEAR)->isError($message)) {
+            $error = $message;
+        } else {
+            //require_once 'DB/DataObject/Error.php';
+            require_once 'Error.php';
+            $dor = new PEAR();
+            $error = $dor->raiseError(
+                $message,
+                $type,
+                $behaviour,
+                $opts = null,
+                $userinfo = null,
+                'DB_DataObject_Error'
+            );
         }
-        
-        
-        if (!$this->_query['group_by']) {
-            $this->_query['group_by'] = " GROUP BY {$group} ";
-            return;
+        // this will never work totally with PHP's object model.
+        // as this is passed on static calls (like staticGet in our case)
+
+        $_DB_DATAOBJECT['LASTERROR'] = $error;
+
+        if (isset($this) && is_object($this) && is_subclass_of($this, 'db_dataobject')) {
+            $this->_lastError = $error;
         }
-        $this->_query['group_by'] .= " , {$group}";
+
+        return $error;
     }
 
     /**
-     * Adds a having clause
-     *
-     * $object->having(); //reset the grouping
-     * $object->having("sum(value) > 0 ");
+     * autoload Class
      *
-     * @param  string  $having  condition
-     * @access public
-     * @return none|PEAR::Error - invalid args only
+     * @param string|array $class Class
+     * @param bool $table Table trying to load.
+     * @return string classname on Success
+     * @access private
      */
-    public function having($having = false)
+    public function _autoloadClass($class, $table = false)
     {
-        if ($this->_query === false) {
-            $this->raiseError(
-                "You cannot do two queries on the same object (copy it before finding)",
-                DB_DATAOBJECT_ERROR_INVALIDARGS
-            );
+        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 = $table ? $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 ($having === false) {
-            $this->_query['having'] = '';
-            return;
+        // support for:
+        // class_location = mydir/ => maps to mydir/Tablename.php
+        // class_location = mydir/myfile_%s.php => maps to mydir/myfile_Tablename
+        // with directory sepr
+        // class_location = mydir/:mydir2/: => tries all of thes locations.
+        $cl = $_DB_DATAOBJECT['CONFIG']['class_location'];
+
+
+        switch (true) {
+            case (strpos($cl, '%s') !== false):
+                $file = sprintf($cl, preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)));
+                break;
+
+            case (strpos($cl, PATH_SEPARATOR) !== false):
+                $file = array();
+                foreach (explode(PATH_SEPARATOR, $cl) as $p) {
+                    $file[] = $p . '/' . preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)) . ".php";
+                }
+                break;
+            default:
+                $file = $cl . '/' . preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)) . ".php";
+                break;
         }
-        // check input...= 0 or '    ' == error!
-        if (!trim($having)) {
-            return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+
+        $cls = is_array($class) ? $class : array($class);
+
+        if (is_array($file) || !file_exists($file)) {
+            $found = false;
+
+            $file = is_array($file) ? $file : array($file);
+            $search = implode(PATH_SEPARATOR, $file);
+            foreach ($file as $f) {
+                foreach (explode(PATH_SEPARATOR, '' . PATH_SEPARATOR . ini_get('include_path')) as $p) {
+                    $ff = empty($p) ? $f : "$p/$f";
+
+                    if (file_exists($ff)) {
+                        $file = $ff;
+                        $found = true;
+                        break;
+                    }
+                }
+                if ($found) {
+                    break;
+                }
+            }
+            if (!$found) {
+                $dor = new DB_DataObject();
+                $dor->raiseError(
+                    "autoload:Could not find class " . implode(',', $cls) .
+                    " using class_location value :" . $search .
+                    " using include_path value :" . ini_get('include_path'),
+                    DB_DATAOBJECT_ERROR_INVALIDCONFIG
+                );
+                return false;
+            }
         }
-        
-        
-        if (!$this->_query['having']) {
-            $this->_query['having'] = " HAVING {$having} ";
-            return;
+
+        include_once $file;
+
+
+        $ce = false;
+        foreach ($cls as $c) {
+            $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($c, false) : class_exists($c);
+            if ($ce) {
+                $class = $c;
+                break;
+            }
         }
-        $this->_query['having'] .= " AND {$having}";
+        if (!$ce) {
+            $dor = new DB_DataObject();
+            $dor->raiseError(
+                "autoload:Could not autoload " . implode(',', $cls),
+                DB_DATAOBJECT_ERROR_INVALIDCONFIG
+            );
+            return false;
+        }
+        return $class;
     }
 
     /**
-     * Adds a using Index
+     * connects to the database
      *
-     * $object->useIndex(); //reset the use Index
-     * $object->useIndex("some_index");
      *
-     * Note do not put unfiltered user input into theis method.
-     * This is mysql specific at present? - might need altering to support other databases.
+     * 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.
      *
-     * @param  string|array  $index  index or indexes to use.
-     * @access public
-     * @return none|PEAR::Error - invalid args only
+     * @access private
+     * @return error|PEAR|true
      */
-    public function useIndex($index = false)
+    public function _connect()
     {
-        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;
+        global $_DB_DATAOBJECT;
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            $this->_loadConfig();
         }
-        if ($index=== false) {
-            $this->_query['useindex'] = '';
-            return;
+        // 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])) {
+
+            // connection is an error...
+            if ((new 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 (empty($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;
         }
-        // check input...= 0 or '    ' == error!
-        if ((is_string($index) && !trim($index)) || (is_array($index) && !count($index))) {
-            return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+
+        // it's not currently connected!
+        // try and work out what to use for the dsn !
+
+        $options = $_DB_DATAOBJECT['CONFIG'];
+        // if the databse dsn dis defined in the object..
+        $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
+
+        if (!$dsn) {
+            if (!$this->_database && !strlen($this->tableName())) {
+                $this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
+            }
+            if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+                $this->debug("Checking for database specific ini ('{$this->_database}') : database_{$this->_database} in options", "CONNECT");
+            }
+
+            if ($this->_database && !empty($options["database_{$this->_database}"])) {
+                $dsn = $options["database_{$this->_database}"];
+            } elseif (!empty($options['database'])) {
+                $dsn = $options['database'];
+            }
         }
-        $index = is_array($index) ? implode(', ', $index) : $index;
-        
-        if (!$this->_query['useindex']) {
-            $this->_query['useindex'] = " USE INDEX ({$index}) ";
-            return;
+
+        // 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 TP DATABASE :" . $this->_database, "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 = (new 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 = (new 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(print_r($_DB_DATAOBJECT['CONNECTIONS'], true), "CONNECT", 5);
         }
-        $this->_query['useindex'] =  substr($this->_query['useindex'], 0, -2) . ", {$index}) ";
+        if ((new 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 (empty($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 :)
+
+        return true;
     }
+
     /**
-     * Sets the Limit
-     *
-     * $boject->limit(); // clear limit
-     * $object->limit(12);
-     * $object->limit(12,10);
+     * Return or assign the name of the current table
      *
-     * 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
+     * @param string optinal table name to set
      * @access public
-     * @return none|PEAR::Error - invalid args only
+     * @return string The name of the current table
      */
-    public function limit($a = null, $b = null)
+    public function tableName()
     {
-        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;
+        global $_DB_DATAOBJECT;
+        $args = func_get_args();
+        if (count($args)) {
+            $this->__table = $args[0];
         }
-        
-        if ($a === null) {
-            $this->_query['limit_start'] = '';
-            $this->_query['limit_count'] = '';
-            return;
+        if (empty($this->__table)) {
+            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);
+        if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
+            return strtolower($this->__table);
         }
-        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;
+        return $this->__table;
     }
 
     /**
-     * Adds a select columns
+     * Get a result using key, value.
      *
-     * $object->selectAdd(); // resets select to nothing!
-     * $object->selectAdd("*"); // default select
-     * $object->selectAdd("unixtime(DATE) as udate");
-     * $object->selectAdd("DATE");
+     * 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
      *
-     * to prepend distict:
-     * $object->selectAdd('distinct ' . $object->selectAdd());
+     * see the fetch example on how to extend this.
      *
-     * @param  string  $k
-     * @access public
-     * @return mixed null or old string if you reset it.
+     * 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
      */
-    public function selectAdd($k = null)
+    public function get($k = null, $v = 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;
+        global $_DB_DATAOBJECT;
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            DB_DataObject::_loadConfig();
         }
-        if ($k === null) {
-            $old = $this->_query['data_select'];
-            $this->_query['data_select'] = '';
-            return $old;
+        $keys = array();
+
+        if ($v === null) {
+            $v = $k;
+            $keys = $this->keys();
+            if (!$keys) {
+                $this->raiseError("No Keys available for {$this->tableName()}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+                return false;
+            }
+            $k = $keys[0];
         }
-        
-        // check input...= 0 or '    ' == error!
-        if (!trim($k)) {
-            return $this->raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            $this->debug("$k $v " . print_r($keys, true), "GET");
         }
-        
-        if ($this->_query['data_select']) {
-            $this->_query['data_select'] .= ', ';
+
+        if ($v === null) {
+            $this->raiseError("No Value specified for get", DB_DATAOBJECT_ERROR_INVALIDARGS);
+            return false;
         }
-        $this->_query['data_select'] .= " $k ";
+        $this->$k = $v;
+        return $this->find(1);
     }
+
     /**
-     * Adds multiple Columns or objects to select with formating.
+     * get/set an  array of table primary keys
      *
-     * $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
+     * set usage: $do->keys('id','code');
      *
-     * @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.
+     * 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 public
-     * @return void
+     * @return array
      */
-    public function selectAs($from = null, $format = '%s', $tableName=false)
+    public function keys()
     {
-        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->tableName();
-        if (is_object($from)) {
-            $table = $from->tableName();
-            $from = array_keys($from->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_keys = $args;
         }
-        
-        if ($tableName !== false) {
-            $table = $tableName;
+        if (isset($this->_database_keys)) {
+            return $this->_database_keys;
         }
-        $s = '%s';
-        if (!empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers'])) {
+
+        global $_DB_DATAOBJECT;
+        if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
             $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));
+        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName() . "__keys"])) {
+            return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName() . "__keys"]);
         }
-        $this->_query['data_select'] .= "\n";
+        $this->databaseStructure();
+
+        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName() . "__keys"])) {
+            return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName() . "__keys"]);
+        }
+        return array();
     }
+
     /**
-     * Insert the current objects variables into the database
+     * Autoload or manually load the table definitions
      *
-     * Returns the ID of the inserted element (if auto increment or sequences are used.)
      *
-     * for example
+     * usage :
+     * DB_DataObject::databaseStructure(  'databasename',
+     *                                    parse_ini_file('mydb.ini',true),
+     *                                    parse_ini_file('mydb.link.ini',true));
      *
-     * Designed to be extended
+     * obviously you dont have to use ini files.. (just return array similar to ini files..)
      *
-     * $object = new mytable();
-     * $object->name = "fred";
-     * echo $object->insert();
+     * 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 mixed false on failure, int when auto increment or sequence used, otherwise true on success
+     * @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)
      */
-    public function insert()
+    public function databaseStructure()
     {
         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 = $this->table();
-            
-        if (!$items) {
-            $this->raiseError(
-                "insert:No table definition for {$this->tableName()}",
-                DB_DATAOBJECT_ERROR_INVALIDCONFIG
-            );
-            return false;
-        }
-        $options = $_DB_DATAOBJECT['CONFIG'];
 
+        // Assignment code
 
-        $datasaved = 1;
-        $leftq     = '';
-        $rightq    = '';
-     
-        $seqKeys   = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]) ?
-                        $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] :
-                        $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..
+        if ($args = func_get_args()) {
+            if (count($args) == 1) {
 
-        // big check for using sequences
-        
-        if (($key !== false) && !$useNative) {
-            if (!$seq) {
-                $keyvalue =  $DB->nextId($this->tableName());
+                // 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';
+                    require_once 'Generator.php';
+
+                foreach ($tables as $table) {
+                    $y = new DB_DataObject_Generator;
+                    $y->fillTableSchema($x->_database, $table);
+                }
+                return $_DB_DATAOBJECT['INI'][$x->_database];
             } 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;
-        }
-        
-        // if we haven't set disable_null_strings to "full"
-        $ignore_null = !isset($options['disable_null_strings'])
-                    || !is_string($options['disable_null_strings'])
-                    || strtolower($options['disable_null_strings']) !== 'full' ;
-                    
-             
-        foreach ($items as $k => $v) {
-            
-            // if we are using autoincrement - skip the column...
-            if ($key && ($k == $key) && $useNative) {
-                continue;
-            }
-        
-             
-            // Ignore INTEGERS which aren't set to a value - or empty string..
-            if ((!isset($this->$k) || ($v == 1 && $this->$k === ''))
-                    && $ignore_null
-            ) {
-                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_object($this->$k) && 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;
+                $_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];
                 }
-                $rightq .=  $value;
-                continue;
-            }
-            
-            
-            if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this, $k)) {
-                $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');
+                return true;
             }
-            
-            // 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->tableName())    : $this->tableName());
-            
-            
-            if (($dbtype == 'pgsql') && empty($leftq)) {
-                $r = $this->_query("INSERT INTO {$table} DEFAULT VALUES");
-            } else {
-                $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->tableName()));
-                        }
-                        $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 (!$this->_database) {
+            $this->_connect();
+        }
 
-            if (isset($_DB_DATAOBJECT['CACHE'][strtolower(get_class($this))])) {
-                $this->_clear_cache();
-            }
-            if ($key) {
-                return $this->$key;
-            }
+
+        // if this table is already loaded this table..
+        if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
             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
-     */
-    public function update($dataObject = false)
-    {
-        global $_DB_DATAOBJECT;
-        // connect will load the config!
-        $this->_connect();
-        
-        
-        $original_query =  $this->_query;
-        
-        $items = $this->table();
-        
-        // only apply update against sequence key if it is set?????
-        
-        $seq    = $this->sequenceKey();
-        if ($seq[0] !== false) {
-            $keys = array($seq[0]);
-            if (!isset($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
-                    ". print_r(array('seq' => $seq , 'keys'=>$keys), true), DB_DATAOBJECT_ERROR_INVALIDARGS);
-                return false;
-            }
-        } else {
-            $keys = $this->keys();
+        // initialize the ini data.. if empt..
+        if (empty($_DB_DATAOBJECT['INI'][$this->_database])) {
+            $_DB_DATAOBJECT['INI'][$this->_database] = array();
         }
-        
-         
-        if (!$items) {
-            $this->raiseError("update:No table definition for {$this->tableName()}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
-            return false;
+
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            DB_DataObject::_loadConfig();
         }
-        $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'];
-        
-        
-        $ignore_null = !isset($options['disable_null_strings'])
-                    || !is_string($options['disable_null_strings'])
-                    || strtolower($options['disable_null_strings']) !== 'full' ;
-                    
-      
-        foreach ($items as $k => $v) {
-            
-            // I think this is ignoring empty vlalues
-            if ((!isset($this->$k) || ($v == 1 && $this->$k === ''))
-                    && $ignore_null
-            ) {
-                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_object($this->$k) && 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 (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this, $k)) {
-                $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;
+        // we do not have the data for this table yet...
+
+        // if we are configured to use the proxy..
+
+        if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
+            if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+                $this->debug("Loading Generator to fetch Schema", 1);
             }
-            // at present we only cast to integers
-            // - V2 may store additional data about float/int
-            $settings .= "$kSql = " . intval($this->$k) . ' ';
+            class_exists('DB_DataObject_Generator') ? '' :
+                //require_once 'DB/DataObject/Generator.php';
+                require_once 'Generator.php';
+
+
+            $x = new DB_DataObject_Generator;
+            $x->fillTableSchema($this->_database, $this->tableName());
+            return true;
         }
-         
-        
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug("got keys as ".serialize($keys), 3);
+
+
+        // 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}"]);
         }
-        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;
+
+
+        $_DB_DATAOBJECT['INI'][$this->_database] = array();
+        foreach ($schemas as $ini) {
+            if (file_exists($ini) && is_file($ini)) {
+                $_DB_DATAOBJECT['INI'][$this->_database] = array_merge(
+                    $_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);
+                }
             }
         }
-        
-        
-        
-        //  echo " $settings, $this->condition ";
-        if ($settings && isset($this->_query) && $this->_query['condition']) {
-            $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
-        
-            $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;
+        // are table name lowecased..
+        if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
+            foreach ($_DB_DATAOBJECT['INI'][$this->_database] as $k => $v) {
+                // results in duplicate cols.. but not a big issue..
+                $_DB_DATAOBJECT['INI'][$this->_database][strtolower($k)] = $v;
             }
+        }
 
-            $this->_clear_cache();
-            return $r;
+
+        // now have we loaded the structure..
+
+        if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
+            return true;
         }
-        // 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) {
+        // - if not try building it..
+        if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
+            class_exists('DB_DataObject_Generator') ? '' :
+                //require_once 'DB/DataObject/Generator.php';
+                require_once 'Generator.php';
+
+            $x = new DB_DataObject_Generator;
+            $x->fillTableSchema($this->_database, $this->tableName());
+            // should this fail!!!???
             return true;
         }
-        
-        $this->raiseError(
-            "update: No Data specifed for query $settings , {$this->_query['condition']}",
-            DB_DATAOBJECT_ERROR_NODATA
-        );
+        $this->debug("Cant find database schema: {$this->_database}/{$this->tableName()} \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;
     }
 
     /**
-     * Deletes items from table which match current objects variables
-     *
-     * Returns the true on success
+     * find results, either normal or crosstable
      *
      * for example
      *
-     * Designed to be extended
-     *
      * $object = new mytable();
-     * $object->ID=123;
-     * echo $object->delete(); // builds a conditon
+     * $object->ID = 1;
+     * $object->find();
      *
-     * $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.
+     * will set $object->N to number of rows, and expects next command to fetch rows
+     * will return $object->N
      *
-     * @access public
-     * @return mixed Int (No. of rows affected) on success, false on failure, 0 on no data affected
+     * if an error occurs $object->N will be set to false and return value will also be false;
+     * if numRows is not supported it will
+     *
+     *
+     * @param boolean $n Fetch first result
+     * @access  public
+     * @return  mixed (number of rows returned, or true if numRows fetching is not supported)
      */
-    public function delete($useWhere = false)
+    public function find($n = 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->tableName()) : $this->tableName());
-            $sql = "DELETE ";
-            // using a joined delete. - with useWhere..
-            $sql .= (!empty($this->_join) && $useWhere) ?
-                "{$table} FROM {$table} {$this->_join} " :
-                "FROM {$table} ";
-                
-            $sql .= $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);
+        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;
         }
-    }
 
-    /**
-     * 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
-     */
-    public function fetchRow($row = null)
-    {
-        global $_DB_DATAOBJECT;
         if (empty($_DB_DATAOBJECT['CONFIG'])) {
-            $this->_loadConfig();
-        }
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug("{$this->tableName()} $row of {$this->N}", "fetchrow", 3);
-        }
-        if (!$this->tableName()) {
-            $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->tableName()} $row of {$this->N}", "fetchrow", 3);
+            DB_DataObject::_loadConfig();
         }
 
-
-        $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;
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            $this->debug($n, "find", 1);
         }
-        $replace = array('.', ' ');
-        foreach ($array as $k => $v) {
-            // use strpos as str_replace is slow.
-            $kk =  (strpos($k, '.') === false && strpos($k, ' ') === false) ?
-                $k : str_replace($replace, '_', $k);
-            
-            if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-                $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
-            }
-            $this->$kk = $array[$k];
+        if (!strlen($this->tableName())) {
+            // 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());
 
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug("{$this->tableName()} 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
-     */
-    public 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);
+
+        $sql = $this->_build_select();
+
+        foreach ($this->_query['unions'] as $union_ar) {
+            $sql .= $union_ar[1] . $union_ar[0]->_build_select() . " \n";
         }
-        $keys = $this->keys();
 
-        if (empty($keys[0]) && (!is_string($countWhat) || (strtoupper($countWhat) == 'DISTINCT'))) {
-            $this->raiseError(
-                "You cannot do run count without keys - use \$do->count('id'), or use \$do->count('distinct id')';",
-                DB_DATAOBJECT_ERROR_INVALIDARGS,
-                PEAR_ERROR_DIE
-            );
-            return false;
+        $sql .= $this->_query['order_by'] . " \n";
+
+
+        /* We are checking for method modifyLimitQuery as it is PEAR DB specific */
+        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']);
+            }
         }
-        $table   = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
-        $key_col = empty($keys[0]) ? '' : (($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)) {
+
+
+        $err = $this->_query($sql);
+        if (is_a($err, 'PEAR_Error')) {
             return false;
         }
-         
-        $result  = $_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
-        $l = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ORDERED);
-        // free the results - essential on oracle.
-        $t->free();
+
         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-            $this->debug('Count returned '. $l[0], 1);
+            $this->debug("CHECK autofetchd $n", "find", 1);
         }
-        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
-     */
-    public function query($string)
-    {
-        return $this->_query($string);
-    }
+        // 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]);
+        }
 
-    /**
-     * 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
-     */
-    public 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);
+        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;
     }
 
@@ -1806,779 +1257,342 @@ class DB_DataObject extends DB_DataObject_Overload
     /* ==================================================== */
 
     /**
-     * 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..
+     * 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
-     * @var     string
+     * @return  string
      */
-    public $_database_dsn = '';
+    public function _build_condition($keys, $filter = array(), $negative_filter = array())
+    {
+        global $_DB_DATAOBJECT;
+        $this->_connect();
+        $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
 
-    /**
-     * The Database connection id (md5 sum of databasedsn)
-     *
-     * @access  private
-     * @var     string
-     */
-    public $_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->tableName()) . '.' . $DB->quoteIdentifier($k))
+                : "{$this->tableName()}.{$k}";
+
+
+            if (is_object($this->$k) && is_a($this->$k, 'DB_DataObject_Cast')) {
+                $dbtype = $DB->dsn["phptype"];
+                $value = $this->$k->toString($v, $DB);
+                if ((new 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 (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this, $k)) {
+                $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));
+        }
+        return "";
+    }
 
     /**
-     * The Database name
-     * created in __connection
+     * Adds a condition to the WHERE statement, defaults to AND
      *
-     * @access  private
-     * @var  string
+     * $object->whereAdd(); //reset or cleaer ewhwer
+     * $object->whereAdd("ID > 20");
+     * $object->whereAdd("age > 20","OR");
+     *
+     * @param bool $cond condition
+     * @param string $logic optional logic "OR" (defaults to "AND")
+     * @return   string|PEAR::Error - previous condition or Error when invalid args found
+     * @access   public
      */
-    public $_database = '';
+    public 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;
+    }
 
-    
-    
     /**
-     * The QUERY rules
-     * This replaces alot of the private variables
-     * used to build a query, it is unset after find() is run.
+     * Evaluate whether or not a value is set to null, taking the 'disable_null_strings' option into account.
+     * If the value is a string set to "null" and the "disable_null_strings" option is not set to
+     * true, then the value is considered to be null.
+     * If the value is actually a PHP NULL value, and "disable_null_strings" has been set to
+     * the value "full", then it will also be considered null. - this can not differenticate between not set
      *
      *
-     *
-     * @access  private
-     * @var     array
+     * @param object|array $obj_or_ar
+     * @param string|false $prop prperty
+     * @access private
+     * @return bool  object
      */
-    public $_query = array(
-        'condition'   => '', // the WHERE condition
-        'group_by'    => '', // the GROUP BY condition
-        'order_by'    => '', // the ORDER BY condition
-        'having'      => '', // the HAVING condition
-        'useindex'   => '', // the USE INDEX condition
-        'limit_start' => '', // the LIMIT condition
-        'limit_count' => '', // the LIMIT condition
-        'data_select' => '*', // the columns to be SELECTed
-        'unions'      => array(), // the added unions,
-        'derive_table' => '', // derived table name (BETA)
-        'derive_select' => '', // derived table select (BETA)
-    );
-        
-    
-  
+    public function _is_null($obj_or_ar, $prop)
+    {
+        global $_DB_DATAOBJECT;
+
+
+        $isset = $prop === false ? isset($obj_or_ar) :
+            (is_array($obj_or_ar) ? isset($obj_or_ar[$prop]) : isset($obj_or_ar->$prop));
+
+        $value = $isset ?
+            ($prop === false ? $obj_or_ar :
+                (is_array($obj_or_ar) ? $obj_or_ar[$prop] : $obj_or_ar->$prop))
+            : null;
+
+
+        $options = $_DB_DATAOBJECT['CONFIG'];
+
+        $null_strings = !isset($options['disable_null_strings'])
+            || $options['disable_null_strings'] === false;
+
+        $crazy_null = isset($options['disable_null_strings'])
+            && is_string($options['disable_null_strings'])
+            && strtolower($options['disable_null_strings'] === 'full');
+
+        if ($null_strings && $isset && is_string($value) && (strtolower($value) === 'null')) {
+            return true;
+        }
+
+        if ($crazy_null && !$isset) {
+            return true;
+        }
+
+        return false;
+    }
 
     /**
-     * Database result id (references global $_DB_DataObject[results]
+     * backend wrapper for quoting, as MDB2 and DB do it differently...
      *
-     * @access  private
-     * @var     integer
+     * @access private
+     * @param $str
+     * @return string quoted
      */
-    public $_DB_resultid;
-     
-    /**
-    * ResultFields - on the last call to fetch(), resultfields is sent here,
-    * so we can clean up the memory.
-    *
-    * @access  public
-    * @var     array
-    */
-    public $_resultFields = false;
-
 
-    /* ============================================================== */
-    /*  Table definition layer (started of very private but 'came out'*/
-    /* ============================================================== */
+    public 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);
+    }
 
     /**
-     * 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
+     * get/set an associative array of table columns
      *
      * @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)
+     * @param array key=>type array
+     * @return array (associative)
      */
-    public function databaseStructure()
+    public function table()
     {
-        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;
-            }
+
+        // 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 (!$this->_database) {
-            $this->_connect();
+        if (isset($this->_database_fields)) {
+            return $this->_database_fields;
         }
-        
-        
-        // if this table is already loaded this table..
-        if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
-            return true;
+
+
+        global $_DB_DATAOBJECT;
+        if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
+            $this->_connect();
         }
-        
-        // initialize the ini data.. if empt..
-        if (empty($_DB_DATAOBJECT['INI'][$this->_database])) {
-            $_DB_DATAOBJECT['INI'][$this->_database] = array();
+
+        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
+            return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
         }
-         
-        if (empty($_DB_DATAOBJECT['CONFIG'])) {
-            DB_DataObject::_loadConfig();
+
+        $this->databaseStructure();
+
+
+        $ret = array();
+        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
+            $ret = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
         }
-        
-        // we do not have the data for this table yet...
-        
-        // if we are configured to use the proxy..
-        
-        if (!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->tableName());
-            return true;
-        }
-            
-             
-       
-        
-        // 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}"]);
-        }
-                    
-         
-        $_DB_DATAOBJECT['INI'][$this->_database] = array();
-        foreach ($schemas as $ini) {
-            if (file_exists($ini) && is_file($ini)) {
-                $_DB_DATAOBJECT['INI'][$this->_database] = array_merge(
-                    $_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);
-                }
-            }
-        }
-        // are table name lowecased..
-        if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
-            foreach ($_DB_DATAOBJECT['INI'][$this->_database] as $k=>$v) {
-                // results in duplicate cols.. but not a big issue..
-                $_DB_DATAOBJECT['INI'][$this->_database][strtolower($k)] = $v;
-            }
-        }
-        
-        
-        // now have we loaded the structure..
-        
-        if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
-            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->tableName());
-            // should this fail!!!???
-            return true;
-        }
-        $this->debug("Cant find database schema: {$this->_database}/{$this->tableName()} \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
-     */
-    public function tableName()
-    {
-        global $_DB_DATAOBJECT;
-        $args = func_get_args();
-        if (count($args)) {
-            $this->__table = $args[0];
-        }
-        if (empty($this->__table)) {
-            return '';
-        }
-        if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
-            return strtolower($this->__table);
-        }
-        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
-     */
-    public function database()
-    {
-        $args = func_get_args();
-        if (count($args)) {
-            $this->_database = $args[0];
-        } else {
-            $this->_connect();
-        }
-        
-        return $this->_database;
-    }
-  
-    /**
-     * get/set an associative array of table columns
-     *
-     * @access public
-     * @param  array key=>type array
-     * @return array (associative)
-     */
-    public 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->tableName()])) {
-            return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
-        }
-        
-        $this->databaseStructure();
-        
-        $ret = array();
-        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
-            $ret =  $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
-        }
-        
         return $ret;
     }
 
     /**
-     * get/set an  array of table primary keys
-     *
-     * set usage: $do->keys('id','code');
+     * build the basic select query.
      *
-     * 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 public
-     * @return array
+     * @access private
      */
-    public function keys()
+
+    public function _build_select()
     {
-        // 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])) {
+        $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
+        if ($quoteIdentifiers) {
             $this->_connect();
+            $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
         }
-        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
-            return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]);
-        }
-        $this->databaseStructure();
-        
-        if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
-            return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]);
+        $tn = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
+        if (!empty($this->_query['derive_table']) && !empty($this->_query['derive_select'])) {
+
+            // this is a derived select..
+            // not much support in the api yet..
+
+            $sql = 'SELECT ' .
+                $this->_query['derive_select']
+                . ' FROM ( SELECT' .
+                $this->_query['data_select'] . " \n" .
+                " FROM   $tn  " . $this->_query['useindex'] . " \n" .
+                $this->_join . " \n" .
+                $this->_query['condition'] . " \n" .
+                $this->_query['group_by'] . " \n" .
+                $this->_query['having'] . " \n" .
+                ') ' . $this->_query['derive_table'];
+
+            return $sql;
         }
-        return array();
+
+
+        $sql = 'SELECT ' .
+            $this->_query['data_select'] . " \n" .
+            " FROM   $tn  " . $this->_query['useindex'] . " \n" .
+            $this->_join . " \n" .
+            $this->_query['condition'] . " \n" .
+            $this->_query['group_by'] . " \n" .
+            $this->_query['having'] . " \n";
+
+        return $sql;
     }
+
+
+    /* ============================================================== */
+    /*  Table definition layer (started of very private but 'came out'*/
+    /* ============================================================== */
+
     /**
-     * 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.
+     * sends query to database - this is the private one that must work
+     *   - internal functions use this rather than $this->query()
      *
-     * @param  string  optional the key sequence/autoinc. key
-     * @param  boolean optional use native increment. default false
-     * @param  false|string optional native sequence name
-     * @access public
-     * @return array (column,use_native,sequence_name)
+     * @param string $string
+     * @access private
+     * @return mixed none or PEAR_Error
      */
-    public function sequenceKey()
+    public function _query($string)
     {
         global $_DB_DATAOBJECT;
-          
-        // call setting
-        if (!$this->_database) {
-            $this->_connect();
-        }
-        
-        if (!isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database])) {
-            $_DB_DATAOBJECT['SEQUENCE'][$this->_database] = array();
-        }
+        $this->_connect();
 
-        
-        $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->tableName()] = $args;
-        }
-        if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()])) {
-            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()];
-        }
-        // end call setting (eg. $do->sequenceKeys(a,b,c); )
-        
-       
-        
-        
-        $keys = $this->keys();
-        if (!$keys) {
-            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]
-                = array(false,false,false);
+
+        $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");
         }
 
-        $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->tableName()])) {
-            $seqname = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->tableName()];
-            if (strpos($seqname, ':') !== false) {
-                list($usekey, $seqname) = explode(':', $seqname);
+        if (
+            strtoupper($string) == 'BEGIN' ||
+            strtoupper($string) == 'START TRANSACTION'
+        ) {
+            if ($_DB_driver == 'DB') {
+                $DB->autoCommit(false);
+                $DB->simpleQuery('BEGIN');
+            } else {
+                $DB->beginTransaction();
             }
+            return true;
         }
-        
-        
-        // 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->tableName()] = 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->tableName()] = array(false,false,$seqname);
-            }
-            if (is_string($ignore)) {
-                $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',', $ignore);
-            }
-            if (in_array($this->tableName(), $ignore)) {
-                return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
+
+        if (strtoupper($string) == 'COMMIT') {
+            $res = $DB->commit();
+            if ($_DB_driver == 'DB') {
+                $DB->autoCommit(true);
             }
+            return $res;
         }
-        
-        
-        $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__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->tableName()] = array(false,false,$seqname);
-        }
-        // use native sequence keys...
-        // technically postgres native here...
-        // we need to get the new improved tabledata sorted out first.
-        
-        // support named sequence keys.. - currently postgres only..
-        
-        if (in_array($dbtype, array('pgsql')) &&
-                ($table[$usekey] & DB_DATAOBJECT_INT) &&
-                isset($realkeys[$usekey]) && strlen($realkeys[$usekey]) > 1) {
-            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,true, $realkeys[$usekey]);
-        }
-        
-        if (in_array($dbtype, array('pgsql', 'mysql', 'mysqli', 'mssql', 'ifx')) &&
-                ($table[$usekey] & DB_DATAOBJECT_INT) &&
-                isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
-                ) {
-            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = 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->tableName()] = 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
-     */
-    public 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
-     */
-    
-    public 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
-     */
-    public 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])) {
-            
-            // connection is an error...
-            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 (empty($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'];
-        // if the databse dsn dis defined in the object..
-        $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
-        
-        if (!$dsn) {
-            if (!$this->_database && !strlen($this->tableName())) {
-                $this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
-            }
-            if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
-                $this->debug("Checking for database specific ini ('{$this->_database}') : database_{$this->_database} in options", "CONNECT");
-            }
-            
-            if ($this->_database && !empty($options["database_{$this->_database}"])) {
-                $dsn = $options["database_{$this->_database}"];
-            } elseif (!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 TP DATABASE :" .$this->_database, "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(print_r($_DB_DATAOBJECT['CONNECTIONS'], true), "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 (empty($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 :)
-         
-        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
-     */
-    public 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' ||
-            strtoupper($string) == 'START TRANSACTION'
-        ) {
-            if ($_DB_driver == 'DB') {
-                $DB->autoCommit(false);
-                $DB->simpleQuery('BEGIN');
-            } else {
-                $DB->beginTransaction();
-            }
-            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') {
@@ -2586,43 +1600,43 @@ class DB_DataObject extends DB_DataObject_Overload
             }
             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) ;
+            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];
-         
-        
-        for ($tries = 0;$tries < 3;$tries++) {
+        $t = explode(' ', microtime());
+        $_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0] + $t[1];
+
+
+        for ($tries = 0; $tries < 3; $tries++) {
             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;
                 }
             }
-            
+
             // see if we got a failure.. - try again a few times..
             if (!is_object($result) || !is_a($result, 'PEAR_Error')) {
                 break;
@@ -2633,7 +1647,7 @@ class DB_DataObject extends DB_DataObject_Overload
             sleep(1); // wait before retyring..
             $DB->connect($DB->dsn);
         }
-       
+
 
         if (is_object($result) && is_a($result, 'PEAR_Error')) {
             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
@@ -2643,9 +1657,9 @@ class DB_DataObject extends DB_DataObject_Overload
             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);
+            $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':
@@ -2659,8 +1673,8 @@ class DB_DataObject extends DB_DataObject_Overload
         }
         if (is_object($result)) {
             // lets hope that copying the result object is OK!
-            
-            $_DB_resultid  = $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ']++;
+
+            $_DB_resultid = $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ']++;
             $_DB_DATAOBJECT['RESULTS'][$_DB_resultid] = $result;
             $this->_DB_resultid = $_DB_resultid;
         }
@@ -2674,368 +1688,1845 @@ class DB_DataObject extends DB_DataObject_Overload
             } else {
                 $DB->expectError(MDB2_ERROR_UNSUPPORTED);
             }
-            
+
             $this->N = $result->numRows();
             //var_dump($this->N);
-            
+
             if (is_object($this->N) && is_a($this->N, 'PEAR_Error')) {
                 $this->N = true;
             }
             $DB->popExpect();
         }
+        return null;
     }
 
     /**
-     * Builds the WHERE based on the values of of this object
+     * fetches next row into this objects var's
      *
-     * @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
-     */
-    public 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;
+     * 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
+     */
+    public function fetch()
+    {
+        global $_DB_DATAOBJECT;
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            DB_DataObject::_loadConfig();
         }
-       
-                    
-        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 (empty($this->N)) {
+            if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+                $this->debug("No data returned from FIND (eg. N is 0)", "FETCH", 3);
             }
-            if ($negative_filter) {
-                if (in_array($k, $negative_filter)) {
-                    continue;
-                }
+            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)');
             }
-            if (!isset($this->$k)) {
-                continue;
+            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
+                );
             }
-            
-            $kSql = $quoteIdentifiers
-                ? ($DB->quoteIdentifier($this->tableName()) . '.' . $DB->quoteIdentifier($k))
-                : "{$this->tableName()}.{$k}";
-             
-             
-            
-            if (is_object($this->$k) && 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;
+            // 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]);
             }
-            
-            if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this, $k)) {
-                $this->whereAdd(" $kSql  IS NULL");
-                continue;
+            // 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));
+        }
+        $replace = array('.', ' ');
+        foreach ($array as $k => $v) {
+            // use strpos as str_replace is slow.
+            $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
+                $k : str_replace($replace, '_', $k);
+
+            if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+                $this->debug("$kk = " . $array[$k], "fetchrow LINE", 3);
             }
-            
+            $this->$kk = $array[$k];
+        }
 
-            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;
+        // set link flag
+        $this->_link_loaded = false;
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            $this->debug("{$this->tableName()} DONE", "fetchrow", 2);
+        }
+        if (($this->_query !== false) && empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) {
+            $this->_query = false;
+        }
+        return true;
+    }
+
+    /**
+     * Get the value of the primary id
+     *
+     * While I normally use 'id' as the PRIMARY KEY value, some database use
+     * {table}_id as the column name.
+     *
+     * To save a bit of typing,
+     *
+     * $id = $do->pid();
+     *
+     * @return bool|the
+     */
+    public function pid()
+    {
+        $keys = $this->keys();
+        if (!$keys) {
+            $this->raiseError(
+                "No Keys available for {$this->tableName()}",
+                DB_DATAOBJECT_ERROR_INVALIDCONFIG
+            );
+            return false;
+        }
+        $k = $keys[0];
+        if (empty($this->$k)) { // we do not
+            $this->raiseError(
+                "pid() called on Object where primary key value not available",
+                DB_DATAOBJECT_ERROR_NODATA
+            );
+            return false;
+        }
+        return $this->$k;
+    }
+
+    /**
+     * fetches all results as an array,
+     *
+     * return format is dependant on args.
+     * if selectAdd() has not been called on the object, then it will add the correct columns to the query.
+     *
+     * A) Array of values (eg. a list of 'id')
+     *
+     * $x = DB_DataObject::factory('mytable');
+     * $x->whereAdd('something = 1')
+     * $ar = $x->fetchAll('id');
+     * -- returns array(1,2,3,4,5)
+     *
+     * B) Array of values (not from table)
+     *
+     * $x = DB_DataObject::factory('mytable');
+     * $x->whereAdd('something = 1');
+     * $x->selectAdd();
+     * $x->selectAdd('distinct(group_id) as group_id');
+     * $ar = $x->fetchAll('group_id');
+     * -- returns array(1,2,3,4,5)
+     *     *
+     * C) A key=>value associative array
+     *
+     * $x = DB_DataObject::factory('mytable');
+     * $x->whereAdd('something = 1')
+     * $ar = $x->fetchAll('id','name');
+     * -- returns array(1=>'fred',2=>'blogs',3=> .......
+     *
+     * D) array of objects
+     * $x = DB_DataObject::factory('mytable');
+     * $x->whereAdd('something = 1');
+     * $ar = $x->fetchAll();
+     *
+     * E) array of arrays (for example)
+     * $x = DB_DataObject::factory('mytable');
+     * $x->whereAdd('something = 1');
+     * $ar = $x->fetchAll(false,false,'toArray');
+     *
+     *
+     * @param string|false $k key
+     * @param string|false $v value
+     * @param string|false $method method to call on each result to get array value (eg. 'toArray')
+     * @access  public
+     * @return  array  format dependant on arguments, may be empty
+     */
+    public function fetchAll($k = false, $v = false, $method = false)
+    {
+        // should it even do this!!!?!?
+        if ($k !== false &&
+            (   // only do this is we have not been explicit..
+                empty($this->_query['data_select']) ||
+                ($this->_query['data_select'] == '*')
+            )
+        ) {
+            $this->selectAdd();
+            $this->selectAdd($k);
+            if ($v !== false) {
+                $this->selectAdd($v);
             }
-            if (is_numeric($this->$k)) {
-                $this->whereAdd(" $kSql = {$this->$k}");
+        }
+
+        $this->find();
+        $ret = array();
+        while ($this->fetch()) {
+            if ($v !== false) {
+                $ret[$this->$k] = $this->$v;
                 continue;
             }
-            /* this is probably an error condition! */
-            $this->whereAdd(" $kSql = ".intval($this->$k));
+            $ret[] = $k === false ?
+                ($method == false ? clone($this) : $this->$method())
+                : $this->$k;
         }
+        return $ret;
     }
 
-    
-    
     /**
-    * 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 can bedatabasename/table
-    * - and allow modular dataobjects to be written..
-    * (this also helps proxy creation)
-    *
-    * Experimental Support for Multi-Database factory eg. mydatabase.mytable
-    *
-    *
-    * @param  string  $table  tablename (use blank to create a new instance of the same class.)
-    * @access private
-    * @return DataObject|PEAR_Error
-    */
-    
-    
-
-    public static function factory($table = '')
+     * 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.
+     */
+    public function selectAdd($k = null)
     {
-        global $_DB_DATAOBJECT;
-        
-        
-        // multi-database support.. - experimental.
-        $database = '';
-       
-        if (strpos($table, '/') !== false) {
-            list($database, $table) = explode('.', $table, 2);
+        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 ($k === null) {
+            $old = $this->_query['data_select'];
+            $this->_query['data_select'] = '';
+            return $old;
         }
-        // no configuration available for database
-        if (!empty($database) && empty($_DB_DATAOBJECT['CONFIG']['database_'.$database])) {
-            $do = new DB_DataObject();
-            $do->raiseError(
-                "unable to find database_{$database} in Configuration, It is required for factory with database",
-                0,
-                PEAR_ERROR_DIE
-                );
+
+        // check input...= 0 or '    ' == error!
+        if (!trim($k)) {
+            return $this->raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
         }
-        
-       
-        /*
-        if ($table === '') {
-            if (is_a($this,'DB_DataObject') && strlen($this->tableName())) {
-                $table = $this->tableName();
-            } else {
-                return DB_DataObject::raiseError(
-                    "factory did not recieve a table name",
-                    DB_DATAOBJECT_ERROR_INVALIDARGS);
+
+        if ($this->_query['data_select']) {
+            $this->_query['data_select'] .= ', ';
+        }
+        $this->_query['data_select'] .= " $k ";
+        return null;
+    }
+
+    /**
+     * Adds a 'IN' condition to the WHERE statement
+     *
+     * $object->whereAddIn('id', $array, 'int'); //minimal usage
+     * $object->whereAddIn('price', $array, 'float', 'OR');  // cast to float, and call whereAdd with 'OR'
+     * $object->whereAddIn('name', $array, 'string');  // quote strings
+     *
+     * @param string $key key column to match
+     * @param array $list list of values to match
+     * @param string $type string|int|integer|float|bool  cast to type.
+     * @param string $logic optional logic to call whereAdd with eg. "OR" (defaults to "AND")
+     * @access   public
+     * @return   string|PEAR::Error - previous condition or Error when invalid args found
+     */
+    public function whereAddIn($key, $list, $type, $logic = 'AND')
+    {
+        $not = '';
+        if ($key[0] == '!') {
+            $not = 'NOT ';
+            $key = substr($key, 1);
+        }
+        // fix type for short entry.
+        $type = $type == 'int' ? 'integer' : $type;
+
+        if ($type == 'string') {
+            $this->_connect();
+        }
+
+        $ar = array();
+        foreach ($list as $k) {
+            settype($k, $type);
+            $ar[] = $type == 'string' ? $this->_quote($k) : $k;
+        }
+
+        if (!$ar) {
+            return $not ? $this->_query['condition'] : $this->whereAdd("1=0");
+        }
+        return $this->whereAdd("$key $not IN (" . implode(',', $ar) . ')', $logic);
+    }
+
+
+
+    /* =========================================================== */
+    /*  Major Private Methods - the core part!              */
+    /* =========================================================== */
+
+    /**
+     * Adds a order by condition
+     *
+     * $object->orderBy(); //clears order by
+     * $object->orderBy("ID");
+     * $object->orderBy("ID,age");
+     *
+     * @param bool $order Order
+     * @return bool|error|none|PEAR
+     * @access public
+     */
+    public 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 null;
+        }
+        // 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 null;
+        }
+        $this->_query['order_by'] .= " , {$order}";
+        return null;
+    }
+
+    /**
+     * Adds a group by condition
+     *
+     * $object->groupBy(); //reset the grouping
+     * $object->groupBy("ID DESC");
+     * $object->groupBy("ID,age");
+     *
+     * @param bool $group Grouping
+     * @return bool|none|PEAR
+     * @access public
+     */
+    public 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 null;
+        }
+        // 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 null;
+        }
+        $this->_query['group_by'] .= " , {$group}";
+        return null;
+    }
+
+    /**
+     * Adds a having clause
+     *
+     * $object->having(); //reset the grouping
+     * $object->having("sum(value) > 0 ");
+     *
+     * @param bool $having condition
+     * @return bool|none|PEAR
+     * @access public
+     */
+    public 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 null;
+        }
+        // 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 null;
+        }
+        $this->_query['having'] .= " AND {$having}";
+        return null;
+    }
+
+    /**
+     * Adds a using Index
+     *
+     * $object->useIndex(); //reset the use Index
+     * $object->useIndex("some_index");
+     *
+     * Note do not put unfiltered user input into theis method.
+     * This is mysql specific at present? - might need altering to support other databases.
+     *
+     * @param bool $index index or indexes to use.
+     * @return bool|none|PEAR
+     * @access public
+     */
+    public function useIndex($index = 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 ($index === false) {
+            $this->_query['useindex'] = '';
+            return null;
+        }
+        // check input...= 0 or '    ' == error!
+        if ((is_string($index) && !trim($index)) || (is_array($index) && !count($index))) {
+            return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
+        }
+        $index = is_array($index) ? implode(', ', $index) : $index;
+
+        if (!$this->_query['useindex']) {
+            $this->_query['useindex'] = " USE INDEX ({$index}) ";
+            return null;
+        }
+        $this->_query['useindex'] = substr($this->_query['useindex'], 0, -2) . ", {$index}) ";
+        return null;
+    }
+
+    /**
+     * 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
+     * @return bool|none|PEAR
+     * @access public
+     */
+    public 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 null;
+        }
+        // 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;
+        return null;
+    }
+
+    /**
+     * 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
+     */
+    public 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 = $this->table();
+
+        if (!$items) {
+            $this->raiseError(
+                "insert:No table definition for {$this->tableName()}",
+                DB_DATAOBJECT_ERROR_INVALIDCONFIG
+            );
+            return false;
+        }
+        $options = $_DB_DATAOBJECT['CONFIG'];
+
+
+        $datasaved = 1;
+        $leftq = '';
+        $rightq = '';
+
+        $seqKeys = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]) ?
+            $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] :
+            $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->tableName());
+            } else {
+                $f = $DB->getOption('seqname_format');
+                $DB->setOption('seqname_format', '%s');
+                $keyvalue = $DB->nextId($seq);
+                $DB->setOption('seqname_format', $f);
+            }
+            if ((new PEAR)->isError($keyvalue)) {
+                $this->raiseError($keyvalue->toString(), DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+                return false;
+            }
+            $this->$key = $keyvalue;
+        }
+
+        // if we haven't set disable_null_strings to "full"
+        $ignore_null = !isset($options['disable_null_strings'])
+            || !is_string($options['disable_null_strings'])
+            || strtolower($options['disable_null_strings']) !== 'full';
+
+
+        foreach ($items as $k => $v) {
+
+            // if we are using autoincrement - skip the column...
+            if ($key && ($k == $key) && $useNative) {
+                continue;
+            }
+
+
+            // Ignore INTEGERS which aren't set to a value - or empty string..
+            if ((!isset($this->$k) || ($v == 1 && $this->$k === ''))
+                && $ignore_null
+            ) {
+                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_object($this->$k) && is_a($this->$k, 'DB_DataObject_Cast')) {
+                $value = $this->$k->toString($v, $DB);
+                if ((new PEAR)->isError($value)) {
+                    $this->raiseError($value->toString(), DB_DATAOBJECT_ERROR_INVALIDARGS);
+                    return false;
+                }
+                $rightq .= $value;
+                continue;
+            }
+
+
+            if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this, $k)) {
+                $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->tableName()) : $this->tableName());
+
+
+            if (($dbtype == 'pgsql') && empty($leftq)) {
+                $r = $this->_query("INSERT INTO {$table} DEFAULT VALUES");
+            } else {
+                $r = $this->_query("INSERT INTO {$table} ($leftq) VALUES ($rightq) ");
+            }
+
+
+            if ((new 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 ((new PEAR)->isError($mssql_key)) {
+                            $this->raiseError($mssql_key);
+                            return false;
+                        }
+                        $this->$key = $mssql_key;
+                        break;
+
+                    case 'pgsql':
+                        if (!$seq) {
+                            $seq = $DB->getSequenceName(strtolower($this->tableName()));
+                        }
+                        $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
+                        $method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
+                        $pgsql_key = $DB->$method("SELECT currval('" . $seq . "')");
+
+
+                        if ((new 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;
+    }
+
+    /**
+     * 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 public
+     * @return array (column,use_native,sequence_name)
+     */
+    public 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->tableName()] = $args;
+        }
+        if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()])) {
+            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()];
+        }
+        // end call setting (eg. $do->sequenceKeys(a,b,c); )
+
+
+        $keys = $this->keys();
+        if (!$keys) {
+            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]
+                = array(false, false, false);
+        }
+
+
+        $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->tableName()])) {
+            $seqname = $_DB_DATAOBJECT['CONFIG']['sequence_' . $this->tableName()];
+            if (strpos($seqname, ':') !== false) {
+                list($usekey, $seqname) = explode(':', $seqname);
+            }
+        }
+
+
+        // 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->tableName()] = 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->tableName()] = array(false, false, $seqname);
+            }
+            if (is_string($ignore)) {
+                $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',', $ignore);
+            }
+            if (in_array($this->tableName(), $ignore)) {
+                return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false, false, $seqname);
+            }
+        }
+
+
+        $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName() . "__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->tableName()] = array(false, false, $seqname);
+        }
+        // use native sequence keys...
+        // technically postgres native here...
+        // we need to get the new improved tabledata sorted out first.
+
+        // support named sequence keys.. - currently postgres only..
+
+        if (in_array($dbtype, array('pgsql')) &&
+            ($table[$usekey] & DB_DATAOBJECT_INT) &&
+            isset($realkeys[$usekey]) && strlen($realkeys[$usekey]) > 1) {
+            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey, true, $realkeys[$usekey]);
+        }
+
+        if (in_array($dbtype, array('pgsql', 'mysql', 'mysqli', 'mssql', 'ifx')) &&
+            ($table[$usekey] & DB_DATAOBJECT_INT) &&
+            isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
+        ) {
+            return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = 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->tableName()] = array($usekey, false, $seqname);
+    }
+
+    /**
+     * clear the cache values for this class  - normally done on insert/update etc.
+     *
+     * @access private
+     * @return void
+     */
+    public 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]);
+        }
+    }
+
+    /**
+     * 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 bool $dataObject
+     * @return  int rows affected or false on failure
+     * @access public
+     */
+    public function update($dataObject = false)
+    {
+        global $_DB_DATAOBJECT;
+        // connect will load the config!
+        $this->_connect();
+
+
+        $original_query = $this->_query;
+
+        $items = $this->table();
+
+        // only apply update against sequence key if it is set?????
+
+        $seq = $this->sequenceKey();
+        if ($seq[0] !== false) {
+            $keys = array($seq[0]);
+            if (!isset($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
+                    " . print_r(array('seq' => $seq, 'keys' => $keys), true), DB_DATAOBJECT_ERROR_INVALIDARGS);
+                return false;
+            }
+        } else {
+            $keys = $this->keys();
+        }
+
+
+        if (!$items) {
+            $this->raiseError("update:No table definition for {$this->tableName()}", 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'];
+
+
+        $ignore_null = !isset($options['disable_null_strings'])
+            || !is_string($options['disable_null_strings'])
+            || strtolower($options['disable_null_strings']) !== 'full';
+
+
+        foreach ($items as $k => $v) {
+
+            // I think this is ignoring empty vlalues
+            if ((!isset($this->$k) || ($v == 1 && $this->$k === ''))
+                && $ignore_null
+            ) {
+                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_object($this->$k) && is_a($this->$k, 'DB_DataObject_Cast')) {
+                $value = $this->$k->toString($v, $DB);
+                if ((new 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 (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this, $k)) {
+                $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->tableName()) : $this->tableName());
+
+            $r = $this->_query("UPDATE  {$table}  SET {$settings} {$this->_query['condition']} ");
+
+            // restore original query conditions.
+            $this->_query = $original_query;
+
+            if ((new 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 Int (No. of rows affected) on success, false on failure, 0 on no data affected
+     */
+    public 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->tableName()) : $this->tableName());
+            $sql = "DELETE ";
+            // using a joined delete. - with useWhere..
+            $sql .= (!empty($this->_join) && $useWhere) ?
+                "{$table} FROM {$table} {$this->_join} " :
+                "FROM {$table} ";
+
+            $sql .= $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 ((new 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
+     */
+    public function fetchRow($row = null)
+    {
+        global $_DB_DATAOBJECT;
+        if (empty($_DB_DATAOBJECT['CONFIG'])) {
+            $this->_loadConfig();
+        }
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            $this->debug("{$this->tableName()} $row of {$this->N}", "fetchrow", 3);
+        }
+        if (!$this->tableName()) {
+            $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->tableName()} $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;
+        }
+        $replace = array('.', ' ');
+        foreach ($array as $k => $v) {
+            // use strpos as str_replace is slow.
+            $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
+                $k : str_replace($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->tableName()} 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
+     */
+    public 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 (empty($keys[0]) && (!is_string($countWhat) || (strtoupper($countWhat) == 'DISTINCT'))) {
+            $this->raiseError(
+                "You cannot do run count without keys - use \$do->count('id'), or use \$do->count('distinct id')';",
+                DB_DATAOBJECT_ERROR_INVALIDARGS,
+                PEAR_ERROR_DIE
+            );
+            return false;
+        }
+        $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
+        $key_col = empty($keys[0]) ? '' : (($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 ((new 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();
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+            $this->debug('Count returned ' . $l[0], 1);
+        }
+        return (int)$l[0];
+    }
+
+    /**
+     * Free global arrays associated with this object.
+     *
+     *
+     * @access   public
+     * @return   none
+     */
+    public 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();
+        }
+
+        if (is_array($this->_link_loaded)) {
+            foreach ($this->_link_loaded as $do) {
+                if (
+                    !empty($this->{$do}) &&
+                    is_object($this->{$do}) &&
+                    method_exists($this->{$do}, 'free')
+                ) {
+                    $this->{$do}->free();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 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
+     */
+    public 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
+     */
+    public 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;
+    }
+
+    /**
+     * 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
+     */
+    public function database()
+    {
+        $args = func_get_args();
+        if (count($args)) {
+            $this->_database = $args[0];
+        } else {
+            $this->_connect();
+        }
+
+        return $this->_database;
+    }
+
+    /**
+     * generic getter/setter for links
+     *
+     * This is the new 'recommended' way to get get/set linked objects.
+     * must be used with links.ini
+     *
+     * usage:
+     *  get:
+     *  $obj = $do->link('company_id');
+     *  $obj = $do->link(array('local_col', 'linktable:linked_col'));
+     *
+     *  set:
+     *  $do->link('company_id',0);
+     *  $do->link('company_id',$obj);
+     *  $do->link('company_id', array($obj));
+     *
+     *  example function
+     *
+     *  function company() {
+     *     $this->link(array('company_id','company:id'), func_get_args());
+     *   }
+     *
+     *
+     *
+     * @param $field
+     * @param array $set_args
+     * @return mixed true or false on setting, object on getting
+     * @author Alan Knowles
+     * @access public
+     */
+    public function link($field, $set_args = array())
+    {
+        //require_once 'DB/DataObject/Links.php';
+        require_once 'Links.php';
+        $l = new DB_DataObject_Links($this);
+        return $l->link($field, $set_args);
+    }
+
+    /**
+     * load related objects
+     *
+     * Generally not recommended to use this.
+     * The generator should support creating getter_setter methods which are better suited.
+     *
+     * Relies on  <dbname>.links.ini
+     *
+     * Sets properties on the calling dataobject  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.
+     * @return boolean , true on success
+     * @author Tim White <tim@cyface.com>
+     * @access public
+     */
+    public function getLinks($format = '_%s')
+    {
+        //require_once 'DB/DataObject/Links.php';
+        require_once 'Links.php';
+        $l = new DB_DataObject_Links($this);
+        return $l->applyLinks($format);
+    }
+
+    /**
+     * deprecited : @use link()
+     * @param $row
+     * @param null $table
+     * @param bool $link
+     * @return mixed
+     */
+    public function getLink($row, $table = null, $link = false)
+    {
+        //require_once 'DB/DataObject/Links.php';
+        require_once 'Links.php';
+        $l = new DB_DataObject_Links($this);
+        return $l->getLink($row, $table === null ? false : $table, $link);
+    }
+
+    /**
+     * getLinkArray
+     * Fetch an array of related objects. This should be used in conjunction with a <dbname>.links.ini file configuration (see the introduction on linking for details on this).
+     * You may also use this with all parameters to specify, the column and related 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
+     * @param $row
+     * @param string $table - name of table to look up value in
+     * @return array - array of results (empty array on failure)
+     *
+     * Example - Getting the related objects
+     *
+     * $person = new DataObjects_Person;
+     * $person->get(12);
+     * $children = $person->getLinkArray('children');
+     *
+     * echo 'There are ', count($children), ' descendant(s):<br />';
+     * foreach ($children as $child) {
+     *     echo $child->name, '<br />';
+     * }
+     */
+    public function getLinkArray($row, $table = null)
+    {
+        //require_once 'DB/DataObject/Links.php';
+        require_once 'Links.php';
+        $l = new DB_DataObject_Links($this);
+        return $l->getLinkArray($row, $table === null ? false : $table);
+    }
+
+    /**
+     * unionAdd - adds another dataobject to this, building a unioned query.
+     *
+     * usage:
+     * $doTable1 = DB_DataObject::factory("table1");
+     * $doTable2 = DB_DataObject::factory("table2");
+     *
+     * $doTable1->selectAdd();
+     * $doTable1->selectAdd("col1,col2");
+     * $doTable1->whereAdd("col1 > 100");
+     * $doTable1->orderBy("col1");
+     *
+     * $doTable2->selectAdd();
+     * $doTable2->selectAdd("col1, col2");
+     * $doTable2->whereAdd("col2 = 'v'");
+     *
+     * $doTable1->unionAdd($doTable2);
+     * $doTable1->find();
+     *
+     * Note: this model may be a better way to implement joinAdd?, eg. do the building in find?
+     *
+     *
+     * @param             $obj       object|false the union object or false to reset
+     * @param string $is_all string 'ALL' to do all.
+     * @return false|mixed|object
+     */
+
+    public function unionAdd($obj, $is_all = '')
+    {
+        if ($obj === false) {
+            $ret = $this->_query['unions'];
+            $this->_query['unions'] = array();
+            return $ret;
+        }
+        $this->_query['unions'][] = array($obj, 'UNION ' . $is_all . ' ');
+        return $obj;
+    }
+
+    /**
+     * autoJoin - using the links.ini file, it builds a query with all the joins
+     * usage:
+     * $x = DB_DataObject::factory('mytable');
+     * $x->autoJoin();
+     * $x->get(123);
+     *   will result in all of the joined data being added to the fetched object..
+     *
+     * $x = DB_DataObject::factory('mytable');
+     * $x->autoJoin();
+     * $ar = $x->fetchAll();
+     *   will result in an array containing all the data from the table, and any joined tables..
+     *
+     * $x = DB_DataObject::factory('mytable');
+     * $jdata = $x->autoJoin();
+     * $x->selectAdd(); //reset..
+     * foreach($_REQUEST['requested_cols'] as $c) {
+     *    if (!isset($jdata[$c])) continue; // ignore columns not available..
+     *    $x->selectAdd( $jdata[$c] . ' as ' . $c);
+     * }
+     * $ar = $x->fetchAll();
+     *   will result in only the columns requested being fetched...
+     *
+     *
+     *
+     * @param array     Configuration
+     *          exclude  Array of columns to exclude from results (eg. modified_by_id)
+     *          links    The equivilant links.ini data for this table eg.
+     *                    array( 'person_id' => 'person:id', .... )
+     *          include  Array of columns to include
+     *          distinct Array of distinct columns.
+     *
+     * @return   array      info about joins
+     *                      cols => map of resulting {joined_tablename}.{joined_table_column_name}
+     *                      join_names => map of resulting {join_name_as}.{joined_table_column_name}
+     *                      count => the column to count on.
+     * @access   public
+     */
+    public function autoJoin($cfg = array())
+    {
+        global $_DB_DATAOBJECT;
+        //var_Dump($cfg);exit;
+        $pre_links = $this->links();
+        if (!empty($cfg['links'])) {
+            $this->links(array_merge($pre_links, $cfg['links']));
+        }
+        $map = $this->links();
+
+        $this->databaseStructure();
+        $dbstructure = $_DB_DATAOBJECT['INI'][$this->_database];
+        //print_r($map);
+        $tabdef = $this->table();
+
+        // we need this as normally it's only cleared by an empty selectAs call.
+
+
+        $keys = array_keys($tabdef);
+        if (!empty($cfg['exclude'])) {
+            $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
+        }
+        if (!empty($cfg['include'])) {
+            $keys = array_intersect($keys, $cfg['include']);
+        }
+
+        $selectAs = array();
+
+        if (!empty($keys)) {
+            $selectAs = array(array($keys, '%s', false));
+        }
+
+        $ret = array(
+            'cols' => array(),
+            'join_names' => array(),
+            'count' => false,
+        );
+
+
+        $has_distinct = false;
+        if (!empty($cfg['distinct']) && $keys) {
+
+            // reset the columsn?
+            $cols = array();
+
+            //echo '<PRE>' ;print_r($xx);exit;
+            foreach ($keys as $c) {
+                //var_dump($c);
+
+                if ($cfg['distinct'] == $c) {
+                    $has_distinct = 'DISTINCT( ' . $this->tableName() . '.' . $c . ') as ' . $c;
+                    $ret['count'] = 'DISTINCT  ' . $this->tableName() . '.' . $c . '';
+                    continue;
+                }
+                // cols is in our filtered keys...
+                $cols = $c;
             }
+            // apply our filtered version, which excludes the distinct column.
+
+            $selectAs = empty($cols) ? array() : array(array(array($cols), '%s', false));
+        }
+
+        foreach ($keys as $k) {
+            $ret['cols'][$k] = $this->tableName() . '.' . $k;
         }
 
-        */
-        // does this need multi db support??
-        $cp = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
-            explode(PATH_SEPARATOR, $_DB_DATAOBJECT['CONFIG']['class_prefix']) : '';
-        
-        //print_r($cp);
-        
-        // multiprefix support.
-        $tbl = preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table));
-        if (is_array($cp)) {
-            $class = array();
-            foreach ($cp as $cpr) {
-                $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($cpr . $tbl, false) : class_exists($cpr . $tbl);
-                if ($ce) {
-                    $class = $cpr . $tbl;
-                    break;
+
+        foreach ($map as $ocl => $info) {
+            list($tab, $col) = explode(':', $info);
+            // what about multiple joins on the same table!!!
+
+            // if links point to a table that does not exist - ignore.
+            if (!isset($dbstructure[$tab])) {
+                continue;
+            }
+
+            $xx = DB_DataObject::factory($tab);
+            if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
+                continue;
+            }
+            // skip columns that are excluded.
+
+            // we ignore include here... - as
+
+            // this is borked ... for multiple jions..
+            $this->joinAdd($xx, 'LEFT', 'join_' . $ocl . '_' . $col, $ocl);
+
+            if (!empty($cfg['exclude']) && in_array($ocl, $cfg['exclude'])) {
+                continue;
+            }
+
+            $tabdef = $xx->table();
+            $table = $xx->tableName();
+
+            $keys = array_keys($tabdef);
+
+
+            if (!empty($cfg['exclude'])) {
+                $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
+
+                foreach ($keys as $k) {
+                    if (in_array($ocl . '_' . $k, $cfg['exclude'])) {
+                        $keys = array_diff($keys, $k); // removes the k..
+                    }
                 }
-                $class[] = $cpr . $tbl;
-            }
-        } else {
-            $class = $tbl;
-            $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($class, false) : class_exists($class);
-        }
-        
-        
-        $rclass = $ce ? $class  : DB_DataObject::_autoloadClass($class, $table);
-        // proxy = full|light
-        if (!$rclass && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
-            DB_DataObject::debug("FAILED TO Autoload  $database.$table - using proxy.", "FACTORY", 1);
-        
-        
-            $proxyMethod = 'getProxy'.$_DB_DATAOBJECT['CONFIG']['proxy'];
-            // if you have loaded (some other way) - dont try and load it again..
-            class_exists('DB_DataObject_Generator') ? '' :
-                    require_once 'DB/DataObject/Generator.php';
-            
-            $d = new DB_DataObject;
-           
-            $d->__table = $table;
-            
-            $ret = $d->_connect();
-            if (is_object($ret) && is_a($ret, 'PEAR_Error')) {
-                return $ret;
             }
-            
-            $x = new DB_DataObject_Generator;
-            return $x->$proxyMethod($d->_database, $table);
-        }
-        
-        if (!$rclass || !class_exists($rclass)) {
-            $dor = new DB_DataObject();
-            return $dor->raiseError(
-                "factory could not find class " .
-                (is_array($class) ? implode(PATH_SEPARATOR, $class)  : $class).
-                "from $table",
-                DB_DATAOBJECT_ERROR_INVALIDCONFIG
-            );
-        }
-        $ret = new $rclass();
-        if (!empty($database)) {
-            DB_DataObject::debug("Setting database to $database", "FACTORY", 1);
-            $ret->database($database);
-        }
-        return $ret;
-    }
-    /**
-     * autoload Class
-     *
-     * @param  string|array  $class  Class
-     * @param  string  $table  Table trying to load.
-     * @access private
-     * @return string classname on Success
-     */
-    public function _autoloadClass($class, $table=false)
-    {
-        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   = $table ? $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;
-        }
-        // support for:
-        // class_location = mydir/ => maps to mydir/Tablename.php
-        // class_location = mydir/myfile_%s.php => maps to mydir/myfile_Tablename
-        // with directory sepr
-        // class_location = mydir/:mydir2/: => tries all of thes locations.
-        $cl = $_DB_DATAOBJECT['CONFIG']['class_location'];
-        
-        
-        switch (true) {
-            case (strpos($cl, '%s') !== false):
-                $file = sprintf($cl, preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)));
-                break;
-                
-            case (strpos($cl, PATH_SEPARATOR) !== false):
-                $file = array();
-                foreach (explode(PATH_SEPARATOR, $cl) as $p) {
-                    $file[] =  $p .'/'.preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)).".php";
+            if (!empty($cfg['include'])) {
+                // include will basically be BASECOLNAME_joinedcolname
+                $nkeys = array();
+                foreach ($keys as $k) {
+                    if (in_array(sprintf($ocl . '_%s', $k), $cfg['include'])) {
+                        $nkeys[] = $k;
+                    }
                 }
-                break;
-            default:
-                $file = $cl .'/'.preg_replace('/[^A-Z0-9]/i', '_', ucfirst($table)).".php";
-                break;
-        }
-        
-        $cls = is_array($class) ? $class : array($class);
-        
-        if (is_array($file) || !file_exists($file)) {
-            $found = false;
-            
-            $file = is_array($file) ? $file : array($file);
-            $search = implode(PATH_SEPARATOR, $file);
-            foreach ($file as $f) {
-                foreach (explode(PATH_SEPARATOR, '' . PATH_SEPARATOR . ini_get('include_path')) as $p) {
-                    $ff = empty($p) ? $f : "$p/$f";
+                $keys = $nkeys;
+            }
 
-                    if (file_exists($ff)) {
-                        $file = $ff;
-                        $found = true;
-                        break;
+            if (empty($keys)) {
+                continue;
+            }
+            // got distinct, and not yet found it..
+            if (!$has_distinct && !empty($cfg['distinct'])) {
+                $cols = array();
+                foreach ($keys as $c) {
+                    $tn = sprintf($ocl . '_%s', $c);
+
+                    if ($tn == $cfg['distinct']) {
+                        $has_distinct = 'DISTINCT( ' . 'join_' . $ocl . '_' . $col . '.' . $c . ')  as ' . $tn;
+                        $ret['count'] = 'DISTINCT  join_' . $ocl . '_' . $col . '.' . $c;
+                        // var_dump($this->countWhat );
+                        continue;
                     }
+                    $cols[] = $c;
                 }
-                if ($found) {
-                    break;
+
+                if (!empty($cols)) {
+                    $selectAs[] = array($cols, $ocl . '_%s', 'join_' . $ocl . '_' . $col);
                 }
+            } else {
+                $selectAs[] = array($keys, $ocl . '_%s', 'join_' . $ocl . '_' . $col);
             }
-            if (!$found) {
-                $dor = new DB_DataObject();
-                $dor->raiseError(
-                    "autoload:Could not find class " . implode(',', $cls) .
-                    " using class_location value :" . $search .
-                    " using include_path value :" . ini_get('include_path'),
-                    DB_DATAOBJECT_ERROR_INVALIDCONFIG
-                );
-                return false;
+
+            foreach ($keys as $k) {
+                $ret['cols'][sprintf('%s_%s', $ocl, $k)] = $tab . '.' . $k;
+                $ret['join_names'][sprintf('%s_%s', $ocl, $k)] = sprintf('join_%s_%s.%s', $ocl, $col, $k);
             }
         }
-        
-        include_once $file;
-        
-       
-        $ce = false;
-        foreach ($cls as $c) {
-            $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($c, false) : class_exists($c);
-            if ($ce) {
-                $class = $c;
-                break;
-            }
+
+        // fill in the select details..
+        $this->selectAdd();
+
+        if ($has_distinct) {
+            $this->selectAdd($has_distinct);
         }
-        if (!$ce) {
-            $dor = new DB_DataObject();
-            $dor->raiseError(
-                "autoload:Could not autoload " . implode(',', $cls),
-                DB_DATAOBJECT_ERROR_INVALIDCONFIG
-            );
-            return false;
+
+        foreach ($selectAs as $ar) {
+            $this->selectAs($ar[0], $ar[1], $ar[2]);
         }
-        return $class;
+        // restore links..
+        $this->links($pre_links);
+
+        return $ret;
     }
-    
-    
-    
+
     /**
-     * Have the links been loaded?
-     * if they have it contains a array of those variables.
+     * Get the links associate array  as defined by the links.ini file.
      *
-     * @access  private
-     * @var     boolean | array
+     *
+     * 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
+     *           false       - if no links.ini exists for this database (hence try auto_links).
+     * @access   public
+     * @see      DB_DataObject::getLinks(), DB_DataObject::getLink()
      */
-    public $_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"
-    *
-    * @param    array $new_links optional - force update of the links for this table
-    *               You probably want to restore it to it's original state after,
-    *               as modifying here does it for the whole PHP request.
-    *
-    * @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
-    *           false       - if no links.ini exists for this database (hence try auto_links).
-    * @access   public
-    * @see      DB_DataObject::getLinks(), DB_DataObject::getLink()
-    */
-    
+
     public function links()
     {
         global $_DB_DATAOBJECT;
@@ -3044,10 +3535,10 @@ class DB_DataObject extends DB_DataObject_Overload
         }
         // have to connect.. -> otherwise things break later.
         $this->_connect();
-        
+
         // alias for shorter code..
-        $lcfg  = &$_DB_DATAOBJECT['LINKS'];
-        $cfg   =  $_DB_DATAOBJECT['CONFIG'];
+        $lcfg = &$_DB_DATAOBJECT['LINKS'];
+        $cfg = $_DB_DATAOBJECT['CONFIG'];
 
         if ($args = func_get_args()) {
             // an associative array was specified, that updates the current
@@ -3067,11 +3558,11 @@ class DB_DataObject extends DB_DataObject_Overload
             // either no file, or empty..
             return $lcfg[$this->_database] === false ? null : array();
         }
-        
+
         // links are same place as schema by default.
         $schemas = isset($cfg['schema_location']) ?
             array("{$cfg['schema_location']}/{$this->_database}.ini") :
-            array() ;
+            array();
 
         // if ini_* is set look there instead.
         // and support multiple locations.
@@ -3080,15 +3571,15 @@ class DB_DataObject extends DB_DataObject_Overload
                 $cfg["ini_{$this->_database}"] :
                 explode(PATH_SEPARATOR, $cfg["ini_{$this->_database}"]);
         }
-                        
+
         // default to not available.
         $lcfg[$this->_database] = false;
 
         foreach ($schemas as $ini) {
             $links = isset($cfg["links_{$this->_database}"]) ?
-                    $cfg["links_{$this->_database}"] :
-                    str_replace('.ini', '.links.ini', $ini);
-            
+                $cfg["links_{$this->_database}"] :
+                str_replace('.ini', '.links.ini', $ini);
+
             // file really exists..
             if (!file_exists($links) || !is_file($links)) {
                 if (!empty($cfg['debug'])) {
@@ -3106,196 +3597,41 @@ class DB_DataObject extends DB_DataObject_Overload
                 parse_ini_file($links, true)
             );
 
-                        
+
             if (!empty($cfg['debug'])) {
                 $this->debug("Loaded links.ini file: $links", "links", 1);
             }
         }
-        
+
         if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
-            foreach ($lcfg[$this->_database] as $k=>$v) {
+            foreach ($lcfg[$this->_database] as $k => $v) {
                 $nk = strtolower($k);
                 // results in duplicate cols.. but not a big issue..
                 $lcfg[$this->_database][$nk] = isset($lcfg[$this->_database][$nk])
-                    ? $lcfg[$this->_database][$nk]  : array();
-                
-                foreach ($v as $kk =>$vv) {
-                    //var_Dump($vv);exit;
-                    $vv =explode(':', $vv);
-                    $vv[0] = strtolower($vv[0]);
-                    $lcfg[$this->_database][$nk][$kk] = implode(':', $vv);
-                }
-            }
-        }
-        //echo '<PRE>';print_r($lcfg);exit;
-        
-        // if there is no link data at all on the file!
-        // we return null.
-        if ($lcfg[$this->_database] === false) {
-            return null;
-        }
-        
-        if (isset($lcfg[$this->_database][$this->tableName()])) {
-            return $lcfg[$this->_database][$this->tableName()];
-        }
-        
-        return array();
-    }
-    
-    
-    /**
-     * generic getter/setter for links
-     *
-     * This is the new 'recommended' way to get get/set linked objects.
-     * must be used with links.ini
-     *
-     * usage:
-     *  get:
-     *  $obj = $do->link('company_id');
-     *  $obj = $do->link(array('local_col', 'linktable:linked_col'));
-     *
-     *  set:
-     *  $do->link('company_id',0);
-     *  $do->link('company_id',$obj);
-     *  $do->link('company_id', array($obj));
-     *
-     *  example function
-     *
-     *  function company() {
-     *     $this->link(array('company_id','company:id'), func_get_args());
-     *   }
-     *
-     *
-     *
-     * @param  mixed $link_spec              link specification (normally a string)
-     *                                       uses similar rules to  joinAdd() array argument.
-     * @param  mixed $set_value (optional)   int, DataObject, or array('set')
-     * @author Alan Knowles
-     * @access public
-     * @return mixed true or false on setting, object on getting
-     */
-    public function link($field, $set_args = array())
-    {
-        require_once 'DB/DataObject/Links.php';
-        $l = new DB_DataObject_Links($this);
-        return  $l->link($field, $set_args) ;
-    }
-    
-    /**
-     * load related objects
-     *
-     * Generally not recommended to use this.
-     * The generator should support creating getter_setter methods which are better suited.
-     *
-     * Relies on  <dbname>.links.ini
-     *
-     * Sets properties on the calling dataobject  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
-     */
-    public function getLinks($format = '_%s')
-    {
-        require_once 'DB/DataObject/Links.php';
-        $l = new DB_DataObject_Links($this);
-        return $l->applyLinks($format);
-    }
+                    ? $lcfg[$this->_database][$nk] : array();
 
-    /**
-     * deprecited : @use link()
-     */
-    public function getLink($row, $table = null, $link = false)
-    {
-        require_once 'DB/DataObject/Links.php';
-        $l = new DB_DataObject_Links($this);
-        return $l->getLink($row, $table === null ? false: $table, $link);
-    }
+                foreach ($v as $kk => $vv) {
+                    //var_Dump($vv);exit;
+                    $vv = explode(':', $vv);
+                    $vv[0] = strtolower($vv[0]);
+                    $lcfg[$this->_database][$nk][$kk] = implode(':', $vv);
+                }
+            }
+        }
+        //echo '<PRE>';print_r($lcfg);exit;
 
-    /**
-     * getLinkArray
-     * Fetch an array of related objects. This should be used in conjunction with a <dbname>.links.ini file configuration (see the introduction on linking for details on this).
-     * You may also use this with all parameters to specify, the column and related 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
-     * @param string $column - either column or column.xxxxx
-     * @param string $table - name of table to look up value in
-     * @return array - array of results (empty array on failure)
-     *
-     * Example - Getting the related objects
-     *
-     * $person = new DataObjects_Person;
-     * $person->get(12);
-     * $children = $person->getLinkArray('children');
-     *
-     * echo 'There are ', count($children), ' descendant(s):<br />';
-     * foreach ($children as $child) {
-     *     echo $child->name, '<br />';
-     * }
-     *
-     */
-    public function getLinkArray($row, $table = null)
-    {
-        require_once 'DB/DataObject/Links.php';
-        $l = new DB_DataObject_Links($this);
-        return $l->getLinkArray($row, $table === null ? false: $table);
-    }
+        // if there is no link data at all on the file!
+        // we return null.
+        if ($lcfg[$this->_database] === false) {
+            return null;
+        }
 
-    /**
-    * unionAdd - adds another dataobject to this, building a unioned query.
-    *
-    * usage:
-    * $doTable1 = DB_DataObject::factory("table1");
-    * $doTable2 = DB_DataObject::factory("table2");
-    *
-    * $doTable1->selectAdd();
-    * $doTable1->selectAdd("col1,col2");
-    * $doTable1->whereAdd("col1 > 100");
-    * $doTable1->orderBy("col1");
-    *
-    * $doTable2->selectAdd();
-    * $doTable2->selectAdd("col1, col2");
-    * $doTable2->whereAdd("col2 = 'v'");
-    *
-    * $doTable1->unionAdd($doTable2);
-    * $doTable1->find();
-     *
-    * Note: this model may be a better way to implement joinAdd?, eg. do the building in find?
-    *
-    *
-    * @param             $obj       object|false the union object or false to reset
-    * @param    optional $is_all    string 'ALL' to do all.
-    * @returns           $obj       object|array the added object, or old list if reset.
-    */
-    
-    public function unionAdd($obj, $is_all= '')
-    {
-        if ($obj === false) {
-            $ret = $this->_query['unions'];
-            $this->_query['unions'] = array();
-            return $ret;
+        if (isset($lcfg[$this->_database][$this->tableName()])) {
+            return $lcfg[$this->_database][$this->tableName()];
         }
-        $this->_query['unions'][] = array($obj, 'UNION ' . $is_all . ' ') ;
-        return $obj;
-    }
 
-    
-    
-    /**
-     * The JOIN condition
-     *
-     * @access  private
-     * @var     string
-     */
-    public $_join = '';
+        return array();
+    }
 
     /**
      * joinAdd - adds another dataobject to this, building a joined query.
@@ -3323,7 +3659,7 @@ class DB_DataObject extends DB_DataObject_Overload
      * }
      *
      *
-     * @param    optional $obj       object |array    the joining object (no value resets the join)
+     * @param bool $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
@@ -3331,7 +3667,7 @@ class DB_DataObject extends DB_DataObject_Overload
      *                                          array('local_column',  $dataobject , 'remote_column');
      *                                             if array has 3 args, then second is assumed to be the linked dataobject.
      *
-     * @param    optional $joinType  string | array
+     * @param string $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.
@@ -3343,11 +3679,10 @@ class DB_DataObject extends DB_DataObject_Overload
      *                                          'joinCol' => ....
      *                                          'useWhereAsOn' => false,
      *
-     * @param    optional $joinAs    string     if you want to select the table as anther name
+     * @param bool $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
+     * @param bool $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)
@@ -3358,46 +3693,46 @@ class DB_DataObject extends DB_DataObject_Overload
      *                                          into ON arguments.
      *
      *
-     * @return   none
+     * @return error|none
      * @access   public
      * @author   Stijn de Reede      <sjr@gmx.co.uk>
      */
-    public function joinAdd($obj = false, $joinType='INNER', $joinAs=false, $joinCol=false)
+    public function joinAdd($obj = false, $joinType = 'INNER', $joinAs = false, $joinCol = false)
     {
         global $_DB_DATAOBJECT;
         if ($obj === false) {
             $this->_join = '';
-            return;
+            return null;
         }
-         
+
         //echo '<PRE>'; print_r(func_get_args());
         $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';
+            $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];
-            
+
             if (count($obj) == 3) {
                 $ofield = $obj[2];
                 $obj = $obj[1];
             } else {
                 list($toTable, $ofield) = explode(':', $obj[1]);
-            
+
                 $obj = is_string($toTable) ? DB_DataObject::factory($toTable) : $toTable;
-            
+
                 if (!$obj || !is_object($obj) || is_a($obj, 'PEAR_Error')) {
                     $obj = new DB_DataObject;
                     $obj->__table = $toTable;
@@ -3408,17 +3743,17 @@ class DB_DataObject extends DB_DataObject_Overload
             // 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];
-       
+
 
         /// CHANGED 26 JUN 2009 - we prefer links from our local table over the remote one.
-        
+
         /* otherwise see if there are any links from this table to the obj. */
         //print_r($this->links());
         if (($ofield === false) && ($links = $this->links())) {
@@ -3428,12 +3763,11 @@ class DB_DataObject extends DB_DataObject_Overload
             // link contains this_column =  linked_table:linked_column
             foreach ($links as $k => $linkVar) {
                 if (!is_array($linkVar)) {
-                    $linkVar  = array($linkVar);
+                    $linkVar = array($linkVar);
                 }
                 foreach ($linkVar as $v) {
 
-                    
-                    
+
                     /* link contains {this column} = {linked table}:{linked column} */
                     $ar = explode(':', $v);
                     // Feature Request #4266 - Allow joins with multiple keys
@@ -3468,43 +3802,43 @@ class DB_DataObject extends DB_DataObject_Overload
             foreach ($olinks as $k => $linkVar) {
                 /* link contains {this column} = array ( {linked table}:{linked column} )*/
                 if (!is_array($linkVar)) {
-                    $linkVar  = array($linkVar);
+                    $linkVar = array($linkVar);
                 }
                 foreach ($linkVar as $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->tableName()) {
                         continue;
                     }
-                    
+
                     // 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->tableName()}). " .
-                            "Either remove the fourth argument to joinAdd() ".
+                            "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;
@@ -3528,28 +3862,28 @@ class DB_DataObject extends DB_DataObject_Overload
             return false;
         }
         $joinType = strtoupper($joinType);
-        
+
         // we default to joining as the same name (this is remvoed later..)
-        
+
         if ($joinAs === false) {
             $joinAs = $obj->tableName();
         }
-        
+
         $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
         $options = $_DB_DATAOBJECT['CONFIG'];
-        
+
         // not sure  how portable adding database prefixes is..
         $objTable = $quoteIdentifiers ?
-                $DB->quoteIdentifier($obj->tableName()) :
-                 $obj->tableName() ;
-                
-        $dbPrefix  = '';
-        if (strlen($obj->_database) && in_array($DB->dsn['phptype'], array('mysql','mysqli'))) {
+            $DB->quoteIdentifier($obj->tableName()) :
+            $obj->tableName();
+
+        $dbPrefix = '';
+        if (strlen($obj->_database) && in_array($DB->dsn['phptype'], array('mysql', 'mysqli'))) {
             $dbPrefix = ($quoteIdentifiers
-                         ? $DB->quoteIdentifier($obj->_database)
-                         : $obj->_database) . '.';
+                    ? $DB->quoteIdentifier($obj->_database)
+                    : $obj->_database) . '.';
         }
-        
+
         // if they are the same, then dont add a prefix...
         if ($obj->_database == $this->_database) {
             $dbPrefix = '';
@@ -3557,10 +3891,10 @@ class DB_DataObject extends DB_DataObject_Overload
         // 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..
@@ -3568,16 +3902,15 @@ class DB_DataObject extends DB_DataObject_Overload
         // 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->tableName()}",
@@ -3585,31 +3918,31 @@ class DB_DataObject extends DB_DataObject_Overload
                 );
                 return false;
             }
-            
+
             $ignore_null = !isset($options['disable_null_strings'])
-                    || !is_string($options['disable_null_strings'])
-                    || strtolower($options['disable_null_strings']) !== 'full' ;
-            
+                || !is_string($options['disable_null_strings'])
+                || strtolower($options['disable_null_strings']) !== 'full';
+
 
             foreach ($items as $k => $v) {
                 if (!isset($obj->$k) && $ignore_null) {
                     continue;
                 }
-                
+
                 $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
-                
+
                 if (DB_DataObject::_is_null($obj, $k)) {
                     $obj->whereAdd("{$joinAs}.{$kSql} IS NULL");
                     continue;
                 }
-                
+
                 if ($v & DB_DATAOBJECT_STR) {
-                    $obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) (
+                    $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
+                            // 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;
                 }
@@ -3617,18 +3950,18 @@ class DB_DataObject extends DB_DataObject_Overload
                     $obj->whereAdd("{$joinAs}.{$kSql} = {$obj->$k}");
                     continue;
                 }
-                            
+
                 if (is_object($obj->$k) && is_a($obj->$k, 'DB_DataObject_Cast')) {
                     $value = $obj->$k->toString($v, $DB);
-                    if (PEAR::isError($value)) {
+                    if ((new PEAR)->isError($value)) {
                         $this->raiseError($value->getMessage(), DB_DATAOBJECT_ERROR_INVALIDARG);
                         return false;
                     }
                     $obj->whereAdd("{$joinAs}.{$kSql} = $value");
                     continue;
                 }
-                
-                
+
+
                 /* this is probably an error condition! */
                 $obj->whereAdd("{$joinAs}.{$kSql} = 0");
             }
@@ -3650,10 +3983,8 @@ class DB_DataObject extends DB_DataObject_Overload
                 $this->whereAdd($cond);
             }
         }
-    
-        
-        
-        
+
+
         // nested (join of joined objects..)
         $appendJoin = '';
         if ($obj->_join) {
@@ -3666,45 +3997,44 @@ class DB_DataObject extends DB_DataObject_Overload
                 $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->tableName();
-        
+
         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);
+            $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->tableName()) : $obj->tableName()) != $joinAs;
+        $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->tableName()) : $obj->tableName()) != $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;
+            $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
                 $jadd = "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
                 //$this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
@@ -3725,221 +4055,73 @@ class DB_DataObject extends DB_DataObject_Overload
                 //echo $jadd ."\n";
                 $this->_join .= $jadd;
                 break;
-                
+
             case '': // this is just a standard multitable select..
                 $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}";
                 $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}");
         }
-         
-         
+
+
         return true;
     }
 
     /**
-     * autoJoin - using the links.ini file, it builds a query with all the joins
-     * usage:
-     * $x = DB_DataObject::factory('mytable');
-     * $x->autoJoin();
-     * $x->get(123);
-     *   will result in all of the joined data being added to the fetched object..
-     *
-     * $x = DB_DataObject::factory('mytable');
-     * $x->autoJoin();
-     * $ar = $x->fetchAll();
-     *   will result in an array containing all the data from the table, and any joined tables..
-     *
-     * $x = DB_DataObject::factory('mytable');
-     * $jdata = $x->autoJoin();
-     * $x->selectAdd(); //reset..
-     * foreach($_REQUEST['requested_cols'] as $c) {
-     *    if (!isset($jdata[$c])) continue; // ignore columns not available..
-     *    $x->selectAdd( $jdata[$c] . ' as ' . $c);
-     * }
-     * $ar = $x->fetchAll();
-     *   will result in only the columns requested being fetched...
-     *
-     *
+     * Adds multiple Columns or objects to select with formating.
      *
-     * @param     array     Configuration
-     *          exclude  Array of columns to exclude from results (eg. modified_by_id)
-     *          links    The equivilant links.ini data for this table eg.
-     *                    array( 'person_id' => 'person:id', .... )
-     *          include  Array of columns to include
-     *          distinct Array of distinct columns.
+     * $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
      *
-     * @return   array      info about joins
-     *                      cols => map of resulting {joined_tablename}.{joined_table_column_name}
-     *                      join_names => map of resulting {join_name_as}.{joined_table_column_name}
-     *                      count => the column to count on.
-     * @access   public
+     * @param array|object|null the array or object to take column names from.
+     * @param string $format
+     * @param bool $tableName
+     * @return bool|void
+     * @access public
      */
-    public function autoJoin($cfg = array())
+    public function selectAs($from = null, $format = '%s', $tableName = false)
     {
         global $_DB_DATAOBJECT;
-        //var_Dump($cfg);exit;
-        $pre_links = $this->links();
-        if (!empty($cfg['links'])) {
-            $this->links(array_merge($pre_links, $cfg['links']));
-        }
-        $map = $this->links();
-        
-        $this->databaseStructure();
-        $dbstructure = $_DB_DATAOBJECT['INI'][$this->_database];
-        //print_r($map);
-        $tabdef = $this->table();
-         
-        // we need this as normally it's only cleared by an empty selectAs call.
-       
-        
-        $keys = array_keys($tabdef);
-        if (!empty($cfg['exclude'])) {
-            $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
-        }
-        if (!empty($cfg['include'])) {
-            $keys =  array_intersect($keys, $cfg['include']);
-        }
-        
-        $selectAs = array();
-        
-        if (!empty($keys)) {
-            $selectAs = array(array( $keys , '%s', false));
-        }
-        
-        $ret = array(
-            'cols' => array(),
-            'join_names' => array(),
-            'count' => false,
-        );
-        
-        
-        
-        $has_distinct = false;
-        if (!empty($cfg['distinct']) && $keys) {
-            
-            // reset the columsn?
-            $cols = array();
-            
-            //echo '<PRE>' ;print_r($xx);exit;
-            foreach ($keys as $c) {
-                //var_dump($c);
-                
-                if ($cfg['distinct'] == $c) {
-                    $has_distinct = 'DISTINCT( ' . $this->tableName() .'.'. $c .') as ' . $c;
-                    $ret['count'] =  'DISTINCT  ' . $this->tableName() .'.'. $c .'';
-                    continue;
-                }
-                // cols is in our filtered keys...
-                $cols = $c;
-            }
-            // apply our filtered version, which excludes the distinct column.
-            
-            $selectAs = empty($cols) ?  array() : array(array(array(  $cols) , '%s', false)) ;
-        }
-                
-        foreach ($keys as $k) {
-            $ret['cols'][$k] = $this->tableName(). '.' . $k;
-        }
-        
-         
-        
-        foreach ($map as $ocl=>$info) {
-            list($tab, $col) = explode(':', $info);
-            // what about multiple joins on the same table!!!
-            
-            // if links point to a table that does not exist - ignore.
-            if (!isset($dbstructure[$tab])) {
-                continue;
-            }
-            
-            $xx = DB_DataObject::factory($tab);
-            if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
-                continue;
-            }
-            // skip columns that are excluded.
-            
-            // we ignore include here... - as
-             
-            // this is borked ... for multiple jions..
-            $this->joinAdd($xx, 'LEFT', 'join_'.$ocl.'_'. $col, $ocl);
-            
-            if (!empty($cfg['exclude']) && in_array($ocl, $cfg['exclude'])) {
-                continue;
-            }
-            
-            $tabdef = $xx->table();
-            $table = $xx->tableName();
-            
-            $keys = array_keys($tabdef);
-            
-            
-            if (!empty($cfg['exclude'])) {
-                $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
-                
-                foreach ($keys as $k) {
-                    if (in_array($ocl.'_'.$k, $cfg['exclude'])) {
-                        $keys = array_diff($keys, $k); // removes the k..
-                    }
-                }
-            }
-            
-            if (!empty($cfg['include'])) {
-                // include will basically be BASECOLNAME_joinedcolname
-                $nkeys = array();
-                foreach ($keys as $k) {
-                    if (in_array(sprintf($ocl.'_%s', $k), $cfg['include'])) {
-                        $nkeys[] = $k;
-                    }
-                }
-                $keys = $nkeys;
-            }
-            
-            if (empty($keys)) {
-                continue;
-            }
-            // got distinct, and not yet found it..
-            if (!$has_distinct && !empty($cfg['distinct'])) {
-                $cols = array();
-                foreach ($keys as $c) {
-                    $tn = sprintf($ocl.'_%s', $c);
-                      
-                    if ($tn == $cfg['distinct']) {
-                        $has_distinct = 'DISTINCT( ' . 'join_'.$ocl.'_'.$col.'.'.$c .')  as ' . $tn ;
-                        $ret['count'] =  'DISTINCT  join_'.$ocl.'_'.$col.'.'.$c;
-                        // var_dump($this->countWhat );
-                        continue;
-                    }
-                    $cols[] = $c;
-                }
-                
-                if (!empty($cols)) {
-                    $selectAs[] = array($cols, $ocl.'_%s', 'join_'.$ocl.'_'. $col);
-                }
-            } else {
-                $selectAs[] = array($keys, $ocl.'_%s', 'join_'.$ocl.'_'. $col);
-            }
-              
-            foreach ($keys as $k) {
-                $ret['cols'][sprintf('%s_%s', $ocl, $k)] = $tab.'.'.$k;
-                $ret['join_names'][sprintf('%s_%s', $ocl, $k)] = sprintf('join_%s_%s.%s', $ocl, $col, $k);
-            }
+
+        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;
         }
-        
-        // fill in the select details..
-        $this->selectAdd();
-        
-        if ($has_distinct) {
-            $this->selectAdd($has_distinct);
+
+        if ($from === null) {
+            // blank the '*'
+            $this->selectAdd();
+            $from = $this;
         }
-       
-        foreach ($selectAs as $ar) {
-            $this->selectAs($ar[0], $ar[1], $ar[2]);
+
+
+        $table = $this->tableName();
+        if (is_object($from)) {
+            $table = $from->tableName();
+            $from = array_keys($from->table());
         }
-        // restore links..
-        $this->links($pre_links);
-        
-        return $ret;
+
+        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";
     }
-    
+
     /**
      * Factory method for calling DB_DataObject_Cast
      *
@@ -3955,9 +4137,9 @@ class DB_DataObject extends DB_DataObject_Overload
      *
      *
      * @param string $value (or type if used with 2 arguments)
-     * @param string $callvalue (optional) used with date/null etc..
+     * @return mixed
      */
-    
+
     public function sqlValue($value)
     {
         $method = 'sql';
@@ -3965,35 +4147,38 @@ class DB_DataObject extends DB_DataObject_Overload
             $method = $value;
             $value = func_get_arg(1);
         }
-        require_once 'DB/DataObject/Cast.php';
+        //require_once 'DB/DataObject/Cast.php';
+        require_once 'Cast.php';
         return call_user_func(array('DB_DataObject_Cast', $method), $value);
     }
-    
-    
+
+
+    /* ----------------------- Debugger ------------------ */
+
     /**
      * 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...)
+     * @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
+     * @return array|true
      */
-    public function setFrom($from, $format = '%s', $skipEmpty=false)
+    public function setFrom($from, $format = '%s', $skipEmpty = false)
     {
         global $_DB_DATAOBJECT;
-        $keys  = $this->keys();
+        $keys = $this->keys();
         $items = $this->table();
-        
+
         if (!$items) {
             $this->raiseError(
                 "setFrom:Could not find table definition for {$this->tableName()}",
                 DB_DATAOBJECT_ERROR_INVALIDCONFIG
             );
-            return;
+            return null;
         }
         $overload_return = array();
         foreach (array_keys($items) as $k) {
@@ -4003,18 +4188,18 @@ class DB_DataObject extends DB_DataObject_Overload
             if (!$k) {
                 continue; // ignore empty keys!!! what
             }
-            
+
             $chk = is_object($from) &&
                 (
-                    version_compare(phpversion(), "5.1.0", ">=") ?
+                version_compare(phpversion(), "5.1.0", ">=") ?
                     property_exists($from, sprintf($format, $k)) :  // php5.1
                     array_key_exists(sprintf($format, $k), get_class_vars($from)) //older
                 );
             // if from has property ($format($k)
             if ($chk) {
                 $kk = (strtolower($k) == 'from') ? '_from' : $k;
-                if (method_exists($this, 'set'.$kk)) {
-                    $ret = $this->{'set'.$kk}($from->{sprintf($format, $k)});
+                if (method_exists($this, 'set' . $kk)) {
+                    $ret = $this->{'set' . $kk}($from->{sprintf($format, $k)});
                     if (is_string($ret)) {
                         $overload_return[$k] = $ret;
                     }
@@ -4023,22 +4208,22 @@ class DB_DataObject extends DB_DataObject_Overload
                 $this->$k = $from->{sprintf($format, $k)};
                 continue;
             }
-            
+
             if (is_object($from)) {
                 continue;
             }
-            
+
             if (empty($from[sprintf($format, $k)]) && $skipEmpty) {
                 continue;
             }
-            
+
             if (!isset($from[sprintf($format, $k)]) && !DB_DataObject::_is_null($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 (method_exists($this, 'set' . $kk)) {
+                $ret = $this->{'set' . $kk}($from[sprintf($format, $k)]);
                 if (is_string($ret)) {
                     $overload_return[$k] = $ret;
                 }
@@ -4064,6 +4249,114 @@ class DB_DataObject extends DB_DataObject_Overload
         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
+     */
+
+
+    public function fromValue($col, $value)
+    {
+        global $_DB_DATAOBJECT;
+        $options = $_DB_DATAOBJECT['CONFIG'];
+        $cols = $this->table();
+        // dont know anything about this col..
+        if (!isset($cols[$col]) || is_a($value, 'DB_DataObject_Cast')) {
+            $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 ((!($cols[$col] & DB_DATAOBJECT_NOTNULL)) && DB_DataObject::_is_null($value, false)):
+            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 (($cols[$col] & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($value, false)):
+
+                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;
+        }
+    }
+
     /**
      * Returns an associative array from the current data
      * (kind of oblivates the idea behind DataObjects, but
@@ -4074,8 +4367,8 @@ class DB_DataObject extends DB_DataObject_Overload
      *
      * will also return links converted to arrays.
      *
-     * @param   string  sprintf format for array
-     * @param   bool||number    [true = elemnts that have a value set],
+     * @param string  sprintf format for array
+     * @param bool||number    [true = elemnts that have a value set],
      *                          [false = table + returned colums] ,
      *                          [0 = returned columsn only]
      *
@@ -4086,20 +4379,20 @@ class DB_DataObject extends DB_DataObject_Overload
     public function toArray($format = '%s', $hideEmpty = false)
     {
         global $_DB_DATAOBJECT;
-        
+
         // we use false to ignore sprintf.. (speed up..)
         $format = $format == '%s' ? false : $format;
-        
+
         $ret = array();
         $rf = ($this->_resultFields !== false) ? $this->_resultFields :
-                (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ?
-                 $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
-        
+            (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ?
+                $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
+
         $ar = ($rf !== false) ?
             (($hideEmpty === 0) ? $rf : array_merge($rf, $this->table())) :
             $this->table();
 
-        foreach ($ar as $k=>$v) {
+        foreach ($ar as $k => $v) {
             if (!isset($this->$k)) {
                 if (!$hideEmpty) {
                     $ret[$format === false ? $k : sprintf($format, $k)] = '';
@@ -4107,8 +4400,8 @@ class DB_DataObject extends DB_DataObject_Overload
                 continue;
             }
             // call the overloaded getXXXX() method. - except getLink and getLinks
-            if (method_exists($this, 'get'.$k) && !in_array(strtolower($k), array('links','link'))) {
-                $ret[$format === false ? $k : sprintf($format, $k)] = $this->{'get'.$k}();
+            if (method_exists($this, 'get' . $k) && !in_array(strtolower($k), array('links', 'link'))) {
+                $ret[$format === false ? $k : sprintf($format, $k)] = $this->{'get' . $k}();
                 continue;
             }
             // should this call toValue() ???
@@ -4120,7 +4413,7 @@ class DB_DataObject extends DB_DataObject_Overload
         foreach ($this->_link_loaded as $k) {
             $ret[$format === false ? $k : sprintf($format, $k)] = $this->$k->toArray();
         }
-        
+
         return $ret;
     }
 
@@ -4147,28 +4440,28 @@ class DB_DataObject extends DB_DataObject_Overload
      *
      *
      * @access  public
-     * @return  array of validation results (where key=>value, value=false|object if it failed) or true (if they all succeeded)
+     * @return array|bool
      */
     public function validate()
     {
         global $_DB_DATAOBJECT;
         require_once 'Validate.php';
         $table = $this->table();
-        $ret   = array();
-        $seq   = $this->sequenceKey();
+        $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 ($val & DB_DATAOBJECT_NOTNULL && DB_DataObject::_is_null($this, $key)) {
                 // dont check empty sequence key values..
                 if (($key == $seq[0]) && ($seq[1] == true)) {
@@ -4177,8 +4470,8 @@ class DB_DataObject extends DB_DataObject_Overload
                 $ret[$key] = false;
                 continue;
             }
-            
-            
+
+
             if (DB_DataObject::_is_null($this, $key)) {
                 if ($val & DB_DATAOBJECT_NOTNULL) {
                     $this->debug("'null' field used for '$key', but it is defined as NOT NULL", 'VALIDATION', 4);
@@ -4189,16 +4482,16 @@ class DB_DataObject extends DB_DataObject_Overload
             }
 
             // 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)) {
+            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;
@@ -4206,14 +4499,14 @@ class DB_DataObject extends DB_DataObject_Overload
             // 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);
+                    $ret[$key] = (new Validate)->string($this->$key, VALIDATE_PUNCTUATION . VALIDATE_NAME);
                     break;
                 case  ($val & DB_DATAOBJECT_INT):
-                    $ret[$key] = Validate::number($this->$key, array('decimal'=>'.'));
+                    $ret[$key] = (new Validate)->number($this->$key, array('decimal' => '.'));
                     break;
             }
         }
@@ -4230,7 +4523,7 @@ class DB_DataObject extends DB_DataObject_Overload
      * Gets the DB object related to an object - so you can use funky peardb stuf with it :)
      *
      * @access public
-     * @return object The DB connection
+     * @return bool|object
      */
     public function getDatabaseConnection()
     {
@@ -4245,16 +4538,15 @@ class DB_DataObject extends DB_DataObject_Overload
         }
         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
+     * @return bool|object
      */
-     
+
     public function getDatabaseResult()
     {
         global $_DB_DATAOBJECT;
@@ -4285,14 +4577,18 @@ class DB_DataObject extends DB_DataObject_Overload
      * has problems with 4.3.2RC2 here
      *
      * @access public
+     * @param $method
+     * @param $params
+     * @param $return
      * @return true?
+     * @throws ReflectionException
      * @see overload
      */
 
-    
+
     public function _call($method, $params, &$return)
     {
-        
+
         //$this->debug("ATTEMPTING OVERLOAD? $method");
         // ignore constructors : - mm
         if (strtolower($method) == strtolower(get_class($this))) {
@@ -4303,183 +4599,74 @@ class DB_DataObject extends DB_DataObject_Overload
         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;
+            return true;
         }
-        
+
         $element = substr($method, 3);
-        
+
         // dont you just love php's case insensitivity!!!!
-        
-        $array =  array_keys(get_class_vars($class));
+
+        $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
-    */
-  
-    
-    public function fromValue($col, $value)
-    {
-        global $_DB_DATAOBJECT;
-        $options = $_DB_DATAOBJECT['CONFIG'];
-        $cols = $this->table();
-        // dont know anything about this col..
-        if (!isset($cols[$col]) || is_a($value, 'DB_DataObject_Cast')) {
-            $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 ((!($cols[$col] & DB_DATAOBJECT_NOTNULL)) && DB_DataObject::_is_null($value, false)):
-            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 (($cols[$col] & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($value, false)):
-
-                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...
+            $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;
-            
-            case ($cols[$col] & DB_DATAOBJECT_STR):
-                
-                $this->$col = (string) $value;
-                return true;
-                
-            // todo : floats numerics and ints...
-            default:
-                $this->$col = $value;
-                return true;
+            }
+            // 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 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()
-    */
+     * 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 string|true
+     * @access   public
+     * @see      DB_DataObject::_call(),strftime(),Date::format()
+     */
     public function toValue($col, $format = null)
     {
         if (is_null($format)) {
@@ -4487,7 +4674,7 @@ class DB_DataObject extends DB_DataObject_Overload
         }
         $cols = $this->table();
         switch (true) {
-            case (($cols[$col] & DB_DATAOBJECT_DATE) &&  ($cols[$col] & DB_DATAOBJECT_TIME)):
+            case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
                 if (!$this->$col) {
                     return '';
                 }
@@ -4509,7 +4696,7 @@ class DB_DataObject extends DB_DataObject_Overload
                 require_once 'Date.php';
                 $x = new Date($this->$col);
                 return $x->format($format);
-                
+
             case ($cols[$col] & DB_DATAOBJECT_TIME):
                 if (!$this->$col) {
                     return '';
@@ -4520,317 +4707,37 @@ class DB_DataObject extends DB_DataObject_Overload
                 }
                 // otherwise an error in type...
                 return $this->$col;
-                
-            case ($cols[$col] &  DB_DATAOBJECT_MYSQLTIMESTAMP):
+
+            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
-     */
-    public 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
-     */
-    public static 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)
-     */
-    public $_lastError = false;
+                $x = new Date($this->$col);
 
-    /**
-     * 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
-     */
-    public 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');
-        
-      
-        // 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';
-            $dor = new PEAR();
-            $error = $dor->raiseError(
-                $message,
-                $type,
-                $behaviour,
-                $opts=null,
-                $userinfo=null,
-                'DB_DataObject_Error'
-                        );
-        }
-        // this will never work totally with PHP's object model.
-        // as this is passed on static calls (like staticGet in our case)
-        $_DB_DATAOBJECT['LASTERROR'] = $error;
-        
-        if (isset($this) && is_object($this) && is_subclass_of($this, 'db_dataobject')) {
-            $this->_lastError = $error;
-        }
-   
-        return $error;
-    }
+                return $x->format($format);
 
-    /**
-     * 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
-     */
-    public function _loadConfig()
-    {
-        global $_DB_DATAOBJECT;
 
-        $_DB_DATAOBJECT['CONFIG'] = &PEAR::getStaticProperty('DB_DataObject', 'options');
-    }
-    /**
-    * Free global arrays associated with this object.
-    *
-    *
-    * @access   public
-    * @return   none
-    */
-    public 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();
-        }
+            case ($cols[$col] & DB_DATAOBJECT_BOOL):
 
-        if (is_array($this->_link_loaded)) {
-            foreach ($this->_link_loaded as $do) {
-                if (
-                        !empty($this->{$do}) &&
-                        is_object($this->{$do}) &&
-                        method_exists($this->{$do}, 'free')
-                    ) {
-                    $this->{$do}->free();
+                if ($cols[$col] & DB_DATAOBJECT_STR) {
+                    // it's a 't'/'f' !
+                    return ($this->$col === 't');
                 }
-            }
-        }
-    }
-    /**
-    * Evaluate whether or not a value is set to null, taking the 'disable_null_strings' option into account.
-    * If the value is a string set to "null" and the "disable_null_strings" option is not set to
-    * true, then the value is considered to be null.
-    * If the value is actually a PHP NULL value, and "disable_null_strings" has been set to
-    * the value "full", then it will also be considered null. - this can not differenticate between not set
-    *
-    *
-    * @param  object|array $obj_or_ar
-    * @param  string|false $prop prperty
-
-    * @access private
-    * @return bool  object
-    */
-    public function _is_null($obj_or_ar, $prop)
-    {
-        global $_DB_DATAOBJECT;
-        
-        
-        $isset = $prop === false ? isset($obj_or_ar) :
-            (is_array($obj_or_ar) ? isset($obj_or_ar[$prop]) : isset($obj_or_ar->$prop));
-        
-        $value = $isset ?
-            ($prop === false ? $obj_or_ar :
-                (is_array($obj_or_ar) ? $obj_or_ar[$prop] : $obj_or_ar->$prop))
-            : null;
-        
-        
-        
-        $options = $_DB_DATAOBJECT['CONFIG'];
-        
-        $null_strings = !isset($options['disable_null_strings'])
-                    || $options['disable_null_strings'] === false;
-                    
-        $crazy_null = isset($options['disable_null_strings'])
-                && is_string($options['disable_null_strings'])
-                && strtolower($options['disable_null_strings'] === 'full');
-        
-        if ($null_strings && $isset  && is_string($value)  && (strtolower($value) === 'null')) {
-            return true;
-        }
-        
-        if ($crazy_null && !$isset) {
-            return true;
-        }
-        
-        return false;
-    }
-    
-    /**
-     * (deprecated - use ::get / and your own caching method)
-     */
-    public static function staticGet($class, $k, $v = null)
-    {
-        $lclass = strtolower($class);
-        global $_DB_DATAOBJECT;
-        if (empty($_DB_DATAOBJECT['CONFIG'])) {
-            DB_DataObject::_loadConfig();
-        }
+                return (bool)$this->$col;
 
-        
-
-        $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)) {
-            $dor = new DB_DataObject();
-            $dor->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)) {
-            $dor = new DB_DataObject();
-            $dor->raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
-            
-            $r = false;
-            return $r;
+            default:
+                return sprintf($format, $this->col);
         }
-        $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
-        return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
     }
-    
+
     /**
      * autoload Class relating to a table
      * (deprecited - use ::factory)
      *
-     * @param  string  $table  table
+     * @param string $table table
      * @access private
      * @return string classname on Success
      */
@@ -4843,23 +4750,25 @@ class DB_DataObject extends DB_DataObject_Overload
         $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);
+        $class = $ce ? $class : DB_DataObject::_autoloadClass($class);
         return $class;
     }
-    
+
     /* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
-    
+
     public function _get_table()
     {
         return $this->table();
     }
+
     public 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')) {
index 15ed077660eb9829d6e84d1af2cff8ab29ed6967..b9b22d5e6a465e64efd23d9afa4163e71a4af268 100644 (file)
  * @version    CVS: $Id: Cast.php 287158 2009-08-12 13:58:31Z alan_k $
  * @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');
-*   }
-*
-*
-*
-*
-
-*/
+ *
+ * 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
-    */
+     * Type of data Stored in the object..
+     *
+     * @var string       (date|blob|.....?)
+     * @access public
+     */
     public $type;
-        
+
     /**
-    * Data For date representation
-    *
-    * @var int  day/month/year
-    * @access public
-    */
+     * Data For date representation
+     *
+     * @var int  day/month/year
+     * @access public
+     */
     public $day;
     public $month;
     public $year;
 
-    
+
     /**
-    * Generic Data..
-    *
-    * @var string
-    * @access public
-    */
+     * Generic Data..
+     *
+     * @var string
+     * @access public
+     */
 
     public $value;
-
-
+    /**
+     * Data For time representation ** does not handle timezones!!
+     *
+     * @var int  hour/minute/second
+     * @access public
+     */
+    public $hour;
+    public $minute;
+    public $second;
 
     /**
-    * Blob consructor
-    *
-    * create a Cast object from some raw data.. (binary)
-    *
-    *
-    * @param   string (with binary data!)
-    *
-    * @return   object DB_DataObject_Cast
-    * @access   public
-    */
-  
+     * Blob consructor
+     *
+     * create a Cast object from some raw data.. (binary)
+     *
+     *
+     * @param string (with binary data!)
+     *
+     * @return   object DB_DataObject_Cast
+     * @access   public
+     */
+
     public function blob($value)
     {
         $r = new DB_DataObject_Cast;
@@ -106,19 +113,18 @@ class DB_DataObject_Cast
         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
-    */
-  
+     * 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
+     */
+
     public function string($value)
     {
         $r = new DB_DataObject_Cast;
@@ -126,18 +132,18 @@ class DB_DataObject_Cast
         $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
-    */
-  
+     * 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
+     */
+
     public function sql($value)
     {
         $r = new DB_DataObject_Cast;
@@ -146,36 +152,97 @@ class DB_DataObject_Cast
         return $r;
     }
 
+    /**
+     * 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 bool|object
+     * @access   public
+     * @author   therion 5 at hotmail
+     */
+
+    public function dateTime()
+    {
+        $args = func_get_args();
+        switch (count($args)) {
+            case 0: // no args = now!
+                $datetime = date('Y-m-d G:i:s', mktime());
+
+            // no break
+            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;
+    }
 
     /**
-    * 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
-    */
-  
+     * 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
+     */
+
     public function date()
     {
         $args = func_get_args();
         switch (count($args)) {
             case 0: // no args = today!
-               $bits =  explode('-', date('Y-m-d'));
+                $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 {
@@ -188,11 +255,11 @@ class DB_DataObject_Cast
         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!!!
@@ -210,126 +277,49 @@ class DB_DataObject_Cast
         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
-    */
-    public $hour;
-    public $minute;
-    public $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
-    */
-    
-    public function dateTime()
-    {
-        $args = func_get_args();
-        switch (count($args)) {
-            case 0: // no args = now!
-                $datetime = date('Y-m-d G:i:s', mktime());
-            
-                // no break
-            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
-    */
+     * 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 bool|object
+     * @access   public
+     * @author   therion 5 at hotmail
+     */
     public function time()
     {
         $args = func_get_args();
         switch (count($args)) {
             case 0: // no args = now!
                 $time = date('G:i:s', mktime());
-                
-                // no break
+
+            // no break
             case 1:
                 // continue on from 0 args.
                 if (!isset($time)) {
                     $time = $args[0];
                 }
-                $bits =  explode(':', $time);
+                $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';
@@ -339,171 +329,169 @@ class DB_DataObject_Cast
         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
-    */
-  
-    public function toString($to=false, $db)
+     * get the string to use in the SQL statement for this...
+     *
+     *
+     * @param bool $to Type (DB_DATAOBJECT_*
+     * @param object $db DB Connection Object
+     *
+     *
+     * @return   string
+     * @access   public
+     */
+
+    public function toString($to = false, $db)
     {
         // if $this->type is not set, we are in serious trouble!!!!
         // values for to:
-        $method = 'toStringFrom'.$this->type;
+        $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
-    */
+     * 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
+     */
     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!');
+            return (new 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";
-                
+                return "'" . pg_escape_bytea($this->value) . "'::bytea";
+
             case 'mysql':
-                return "'".mysql_real_escape_string($this->value, $db->connection)."'";
-            
+                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)."'";
-             
+                return "'" . mysqli_real_escape_string($db->connection, $this->value) . "'";
+
             case 'sqlite':
                 // this is funny - the parameter order is reversed ;)
-                return "'".sqlite_escape_string($this->value)."'";
-           
+                return "'" . sqlite_escape_string($this->value) . "'";
+
             case 'mssql':
-                
+
                 if (is_numeric($this->value)) {
                     return $this->value;
                 }
                 $unpacked = unpack('H*hex', $this->value);
                 return '0x' . $unpacked['hex'];
-                        
-                 
-   
+
+
             default:
-                return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
+                return (new 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
-    */
+     * 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
+     */
     public function toStringFromString($to, $db)
     {
         // first weed out invalid casts..
         // in blobs can only be cast to blobs.!
-        
+
         // perhaps we should support TEXT fields???
         //
-        
+
         // $to == a string field which is the default type (0)
         // so we do not test it here. - we assume that number fields
         // will accept a string?? - which is stretching it a bit ...
         // should probaly add that test as some point.
-        
+
         switch ($db->dsn['phptype']) {
             case 'pgsql':
-                return "'".pg_escape_string($this->value)."'::bytea";
-            
+                return "'" . pg_escape_string($this->value) . "'::bytea";
+
             case 'mysql':
-                return "'".mysql_real_escape_string($this->value, $db->connection)."'";
-            
-            
+                return "'" . mysql_real_escape_string($this->value, $db->connection) . "'";
+
+
             case 'mysqli':
-                return "'".mysqli_real_escape_string($db->connection, $this->value)."'";
+                return "'" . mysqli_real_escape_string($db->connection, $this->value) . "'";
 
             case 'mssql':
                 // copied from the old DB mssql code...?? not sure how safe this is.
                 return "'" . str_replace(
-                    array("'", "\\\r\n", "\\\n"),
-                    array("''", "\\\\\r\n\r\n", "\\\\\n\n"),
-                    $this->value
+                        array("'", "\\\r\n", "\\\n"),
+                        array("''", "\\\\\r\n\r\n", "\\\\\n\n"),
+                        $this->value
                     ) . "'";
-                
+
 
             default:
-                return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
+                return (new 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
-    */
+     * 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
+     */
     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!'.
+            return (new 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
-    */
-    
+     * 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
+     */
+
     public function toStringFromDateTime($to, $db)
     {
         // first weed out invalid casts..
@@ -511,7 +499,7 @@ class DB_DataObject_Cast
         // perhaps we should support TEXT fields???
         if (($to !== false) &&
             !($to & (DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME))) {
-            return PEAR::raiseError('Invalid Cast from a ' .
+            return (new PEAR)->raiseError('Invalid Cast from a ' .
                 ' DB_DataObject_Cast::dateTime to something other than a datetime!' .
                 ' (try using native features)');
         }
@@ -519,18 +507,18 @@ class DB_DataObject_Cast
     }
 
     /**
-    * 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
-    */
+     * 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
+     */
 
     public function toStringFromTime($to, $db)
     {
@@ -538,23 +526,23 @@ class DB_DataObject_Cast
         // 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!'.
+            return (new 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
-    */
+     * 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
+     */
     public function toStringFromSql($to, $db)
     {
         return $this->value;
index 677526c57991fddd2fba846a5bd13fb9c631e5de..a72c8ec113430cef4db3e4e0d7cd12c2c813bd70 100644 (file)
  * @version    CVS: $Id: Error.php 287158 2009-08-12 13:58:31Z alan_k $
  * @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
-     *
+     * @param string $message
+     * @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
      * @access public
      *
      * @see PEAR_Error
      */
-    public function DB_DataObject_Error(
+    public function __construct(
         $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...
 }
index 1ac0712a7e53cae0d175c226eede6ff7f557bb09..b81a0b867a73da5ca64c2ba6be10fd0c796ada78 100644 (file)
  * @version    CVS: $Id: Generator.php 336719 2015-05-05 10:37:33Z alan_k $
  * @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!
- */
- /**
+
+/*
+* 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]
@@ -48,7 +48,7 @@
  * 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';
+class_exists('DB_DataObject') ? '' : /*require_once 'DB/DataObject.php'*/ require_once '../DataObject.php';
 //require_once('Config.php');
 
 /**
@@ -93,6 +93,35 @@ class DB_DataObject_Generator extends DB_DataObject
      * @access private
      */
     public $_fkeys; // active tablename
+    /**
+     * Output File was config object, now just string
+     * Used to generate the Tables
+     *
+     * @var    string outputbuffer for table definitions
+     * @access private
+     */
+    public $_newConfig;
+    /**
+     * class being extended (can be overridden by [DB_DataObject] extends=xxxx
+     *
+     * @var    string
+     * @access private
+     */
+    public $_extends = 'DB_DataObject';
+    /**
+     * line to use for require('DB/DataObject.php');
+     *
+     * @var    string
+     * @access private
+     */
+    public $_extendsFile = "DB/DataObject.php";
+    /**
+     * class being generated
+     *
+     * @var    string
+     * @access private
+     */
+    public $_className;
 
     /**
      * The 'starter' = call this to start the process
@@ -102,11 +131,11 @@ class DB_DataObject_Generator extends DB_DataObject
      */
     public function start()
     {
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
 
         $databases = array();
-        foreach ($options as $k=>$v) {
+        foreach ($options as $k => $v) {
             if (substr($k, 0, 9) == 'database_') {
                 $databases[substr($k, 9)] = $v;
             }
@@ -160,41 +189,32 @@ class DB_DataObject_Generator extends DB_DataObject
             }
         }
         $this->debug("DONE\n\n");
+        return null;
     }
 
-    /**
-     * Output File was config object, now just string
-     * Used to generate the Tables
-     *
-     * @var    string outputbuffer for table definitions
-     * @access private
-     */
-    public $_newConfig;
-
     /**
      * Build a list of tables;
      * and store it in $this->tables and $this->_definitions[tablename];
      *
      * @access  private
-     * @return  none
+     * @return none|object
      */
     public function _createTableList()
     {
         $this->_connect();
-        
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
 
-       
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
 
-        $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+
+        $__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_object($__DB) && is_a($__DB, 'PEAR_Error')) {
-            return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
+            return (new 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);
@@ -204,11 +224,11 @@ class DB_DataObject_Generator extends DB_DataObject
             /**
              * set portability and some modules to fetch the informations
              */
-            $db_options = PEAR::getStaticProperty('MDB2', 'options');
+            $db_options = (new PEAR)->getStaticProperty('MDB2', 'options');
             if (empty($db_options)) {
                 $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
             }
-            
+
             $__DB->loadModule('Manager');
             $__DB->loadModule('Reverse');
         }
@@ -230,21 +250,21 @@ class DB_DataObject_Generator extends DB_DataObject
         }
 
         if (is_object($this->tables) && is_a($this->tables, 'PEAR_Error')) {
-            return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
+            return (new 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(is_string($options['build_views']) ?
-                                    $options['build_views'] : 'views');
+                    $options['build_views'] : 'views');
             } else {
                 $views = $__DB->manager->listViews();
             }
             if (is_object($views) && is_a($views, 'PEAR_Error')) {
-                return PEAR::raiseError(
+                return (new PEAR)->raiseError(
                     'Error getting Views (check the PEAR bug database for the fix to DB), ' .
-                $views->toString(),
+                    $views->toString(),
                     null,
                     PEAR_ERROR_DIE
                 );
@@ -258,20 +278,20 @@ class DB_DataObject_Generator extends DB_DataObject
 
         foreach ($this->tables as $table) {
             if (isset($options['generator_include_regex']) &&
-                    !preg_match($options['generator_include_regex'], $table)) {
+                !preg_match($options['generator_include_regex'], $table)) {
                 $this->debug("SKIPPING (generator_include_regex) : $table");
                 continue;
             }
-            
+
             if (isset($options['generator_exclude_regex']) &&
-                    preg_match($options['generator_exclude_regex'], $table)) {
+                preg_match($options['generator_exclude_regex'], $table)) {
                 continue;
             }
-            
+
             $strip = empty($options['generator_strip_schema']) ? false : $options['generator_strip_schema'];
-            $strip = is_numeric($strip) ? (bool) $strip : $strip;
+            $strip = is_numeric($strip) ? (bool)$strip : $strip;
             $strip = (is_string($strip) && strtolower($strip) == 'true') ? true : $strip;
-        
+
             // postgres strip the schema bit from the
             if (!empty($strip)) {
                 if (!is_string($strip) || preg_match($strip, $table)) {
@@ -283,27 +303,26 @@ class DB_DataObject_Generator extends DB_DataObject
                 }
             }
             $this->debug("EXTRACTING : $table");
-            
+
             $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
                 $__DB->quoteIdentifier($table) : $table;
-          
+
             if (!$is_MDB2) {
-                $defs =  $__DB->tableInfo($quotedTable);
+                $defs = $__DB->tableInfo($quotedTable);
             } else {
-                $defs =  $__DB->reverse->tableInfo($quotedTable);
+                $defs = $__DB->reverse->tableInfo($quotedTable);
                 // rename the length value, so it matches db's return.
             }
 
             if (is_object($defs) && is_a($defs, 'PEAR_Error')) {
                 // running in debug mode should pick this up as a big warning..
                 $this->debug("Error reading tableInfo: $table");
-                $this->raiseError('Error reading tableInfo, '. $defs->toString());
+                $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;
@@ -312,7 +331,7 @@ class DB_DataObject_Generator extends DB_DataObject
                 if (isset($def['length']) && !isset($def['len'])) {
                     $def['len'] = $def['length'];
                 }
-                $this->_definitions[$table][] = (object) $def;
+                $this->_definitions[$table][] = (object)$def;
             }
             // we find a matching table, just  store it into a temporary array
             $tmp_table[] = $table;
@@ -320,27 +339,28 @@ class DB_DataObject_Generator extends DB_DataObject
         // 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);
+        return null;
     }
-    
+
     /**
      * Auto generation of table data.
      *
      * it will output to db_oo_{database} the table definitions
      *
      * @access  private
-     * @return  none
+     * @return none|void
      */
     public function generateDefinitions()
     {
         $this->debug("Generating Definitions file:        ");
         if (!$this->tables) {
             $this->debug("-- NO TABLES -- \n");
-            return;
+            return null;
         }
 
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
 
 
         //$this->_newConfig = new Config('IniFile');
@@ -352,21 +372,21 @@ class DB_DataObject_Generator extends DB_DataObject
         // 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;
+            return null;
         }
         if (!empty($options['generator_no_ini'])) { // built in ini files..
-            return;
+            return null;
         }
-        $base =  @$options['schema_location'];
+        $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)));
+            (new System)->mkdir(array('-p', '-m', 0755, dirname($file)));
         }
         $this->debug("Writing ini as {$file}\n");
         //touch($file);
@@ -374,8 +394,8 @@ class DB_DataObject_Generator extends DB_DataObject
         //print_r($this->_newConfig);
         $fh = fopen($tmpname, 'w');
         if (!$fh) {
-            return PEAR::raiseError(
-                "Failed to create temporary file: $tmpname\n".
+            return (new PEAR)->raiseError(
+                "Failed to create temporary file: $tmpname\n" .
                 "make sure session.save_path is set and is writable\n",
                 null,
                 PEAR_ERROR_DIE
@@ -385,7 +405,7 @@ class DB_DataObject_Generator extends DB_DataObject
         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);
@@ -393,195 +413,25 @@ class DB_DataObject_Generator extends DB_DataObject
         chmod($file, $perms);
         //$ret = $this->_newConfig->writeInput($file,false);
 
-        //if (PEAR::isError($ret) ) {
+        //if ((new PEAR)->isError($ret) ) {
         //    return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
         // }
-    }
-    /**
-    * create the data for Foreign Keys (for links.ini)
-    * Currenly only works with mysql / mysqli / posgtreas
-    * to use, you must set option: generate_links=true
-    *
-    * @author Pascal Sch�ni
-    */
-    
-    public function _createForiegnKeys()
-    {
-        $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', 'pgsql'))) {
-            echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
-            return; // cant handle non-mysql introspection for defaults.
-        }
-        $this->debug("generateForeignKeys: Start");
-        $DB = $this->getDatabaseConnection();
-
-        $fk = array();
-
-
-        switch ($DB->phptype) {
-
-
-            case 'pgsql':
-                foreach ($this->tables as $this->table) {
-                    $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?  $DB->quoteIdentifier($table)  : $this->table;
-                    $res =& $DB->query("SELECT
-                                pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
-                            FROM pg_catalog.pg_constraint r,
-                                 pg_catalog.pg_class c
-                            WHERE c.oid=r.conrelid
-                                  AND r.contype = 'f'
-                                  AND c.relname = '" . $quotedTable . "'");
-                    if (PEAR::isError($res)) {
-                        die($res->getMessage());
-                    }
-
-                    while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-                        $treffer = array();
-                        // this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
-                        preg_match(
-                            "/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
-                            $row['condef'],
-                            $treffer
-                        );
-                        if (!count($treffer)) {
-                            continue;
-                        }
-                        $fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
-                    }
-                }
-                break;
-            case 'mysql':
-            case 'mysqli':
-            default:
-
-                foreach ($this->tables as $this->table) {
-                    $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?  $DB->quoteIdentifier($table)  : $this->table;
-                    
-                    $res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable);
-
-                    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)) {
-                        continue;
-                    }
-                    foreach ($treffer as $i=> $tref) {
-                        $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
-                    }
-                }
-
-        }
-     
-        $this->_fkeys = $fk;
-    }
-    
-
-    /**
-     * 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
-     */
-    public 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', 'pgsql'))) {
-            echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
-            return; // cant handle non-mysql introspection for defaults.
-        }
-        $this->debug("generateForeignKeys: Start");
-        
-        $fk = $this->_fkeys;
-        $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'])) {
-            $file = "{$options['schema_location']}/{$this->_database}.links.ini";
-        } elseif (isset($options["ini_{$this->_database}"])) {
-            $file = preg_replace('/\.ini/', '.links.ini', $options["ini_{$this->_database}"]);
-        } else {
-            $this->debug("generateForeignKeys: SKIP - schema_location or ini_{database} was not set");
-            return;
-        }
-         
-
-        if (!file_exists(dirname($file))) {
-            mkdir(dirname($file), 0755, true);
-        }
-
-        $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');
-        if (!$fh) {
-            return PEAR::raiseError(
-                "Failed to create temporary file: $tmpname\n".
-                "make sure session.save_path is set and is writable\n",
-                null,
-                PEAR_ERROR_DIE
-            );
-        }
-        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);
+        return null;
     }
 
-      
     /**
      * The table geneation part
      *
      * @access  private
-     * @return  tabledef and keys array.
+     * @return array|tabledef
      */
     public function _generateDefinitionsTable()
     {
         global $_DB_DATAOBJECT;
-        $options = PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
         $defs = $this->_definitions[$this->table];
         $this->_newConfig .= "\n[{$this->table}]\n";
-        $keys_out =  "\n[{$this->table}__keys]\n";
+        $keys_out = "\n[{$this->table}__keys]\n";
         $keys_out_primary = '';
         $keys_out_secondary = '';
         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
@@ -590,22 +440,21 @@ class DB_DataObject_Generator extends DB_DataObject
         }
         $DB = $this->getDatabaseConnection();
         $dbtype = $DB->phptype;
-        
+
         $ret = array(
-                'table' => array(),
-                'keys' => array(),
-            );
-            
+            'table' => array(),
+            'keys' => array(),
+        );
+
         $ret_keys_primary = array();
         $ret_keys_secondary = array();
-        
-        
-        
+
+
         foreach ($defs as $t) {
-            $n=0;
+            $n = 0;
             $write_ini = true;
-            
-            
+
+
             switch (strtoupper($t->type)) {
 
                 case 'INT':
@@ -621,10 +470,10 @@ class DB_DataObject_Generator extends DB_DataObject
                 case 'BIGINT':
                     $type = DB_DATAOBJECT_INT;
                     if ($t->len == 1) {
-                        $type +=  DB_DATAOBJECT_BOOL;
+                        $type += DB_DATAOBJECT_BOOL;
                     }
                     break;
-               
+
                 case 'REAL':
                 case 'DOUBLE':
                 case 'DOUBLE PRECISION': // double precision (firebird)
@@ -637,142 +486,142 @@ class DB_DataObject_Generator extends DB_DataObject
                 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;
+                        $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 'POINT':       // mysql geometry stuff - not really string - but will do..
-                
+
                 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':
                 case '_TEXT':   //postgres (?? view ??)
-                    
+
                     $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 'BLOB':       /// these should really be ignored!!!???
                 case 'TINYBLOB':
                 case 'MEDIUMBLOB':
                 case 'LONGBLOB':
-                
+
                 case 'CLOB': // oracle character lob support
-                
+
                 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";
+                    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";
+                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";
+
+
+            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";
             $secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';
-            
+
             $m = array();
             if (preg_match('/(auto_increment|nextval\(([^)]*))/i', rawurldecode($t->flags), $m)
                 || (isset($t->autoincrement) && ($t->autoincrement === true))) {
@@ -786,126 +635,237 @@ class DB_DataObject_Generator extends DB_DataObject
                     $keys_out_primary .= "{$t->name} = $sn\n";
                 }
                 $ret_keys_primary[$t->name] = $sn;
-            } elseif ($secondary_key_match && preg_match('/('.$secondary_key_match.')/i', $t->flags)) {
+            } elseif ($secondary_key_match && preg_match('/(' . $secondary_key_match . ')/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;
-    */
-    
-    
-    public 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;
-    */
-    
-    
-    public function getFileNameFromTableName($table)
+     * create the data for Foreign Keys (for links.ini)
+     * Currenly only works with mysql / mysqli / posgtreas
+     * to use, you must set option: generate_links=true
+     *
+     * @author Pascal Sch�ni
+     */
+
+    public function _createForiegnKeys()
     {
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
-        $base = $options['class_location'];
-        if (strpos($base, '%s') !== false) {
-            $base = dirname($base);
+        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
+        if (empty($options['generate_links'])) {
+            return false;
         }
-        if (!file_exists($base)) {
-            require_once 'System.php';
-            System::mkdir(array('-p',$base));
+        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+        if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
+            echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
+            return null; // cant handle non-mysql introspection for defaults.
         }
-        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";
+        $this->debug("generateForeignKeys: Start");
+        $DB = $this->getDatabaseConnection();
+
+        $fk = array();
+
+
+        switch ($DB->phptype) {
+
+
+            case 'pgsql':
+                foreach ($this->tables as $this->table) {
+                    $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($this->table) : $this->table;
+                    $res =& $DB->query("SELECT
+                                pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
+                            FROM pg_catalog.pg_constraint r,
+                                 pg_catalog.pg_class c
+                            WHERE c.oid=r.conrelid
+                                  AND r.contype = 'f'
+                                  AND c.relname = '" . $quotedTable . "'");
+                    if ((new PEAR)->isError($res)) {
+                        die($res->getMessage());
+                    }
+
+                    while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+                        $treffer = array();
+                        // this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
+                        preg_match(
+                            "/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
+                            $row['condef'],
+                            $treffer
+                        );
+                        if (!count($treffer)) {
+                            continue;
+                        }
+                        $fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
+                    }
+                }
+                break;
+
+
+            case 'mysql':
+            case 'mysqli':
+            default:
+
+                foreach ($this->tables as $this->table) {
+                    $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($this->table) : $this->table;
+
+                    $res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable);
+
+                    if ((new 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)) {
+                        continue;
+                    }
+                    foreach ($treffer as $i => $tref) {
+                        $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
+                    }
+                }
+
         }
-        return $outfilename;
+
+
+        $this->_fkeys = $fk;
+    return null;
     }
-    
-    
+
     /**
-    * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
-    *
-    * @access  public
-    * @return  string method name;
-    */
-    
-    
-    public function getMethodNameFromColumnName($col)
+     * 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
+     */
+    public function generateForeignKeys()
     {
-        return ucfirst($col);
+        $options = (new 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', 'pgsql'))) {
+            echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
+            return null; // cant handle non-mysql introspection for defaults.
+        }
+        $this->debug("generateForeignKeys: Start");
+
+        $fk = $this->_fkeys;
+        $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 = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
+
+        if (!empty($options['schema_location'])) {
+            $file = "{$options['schema_location']}/{$this->_database}.links.ini";
+        } elseif (isset($options["ini_{$this->_database}"])) {
+            $file = preg_replace('/\.ini/', '.links.ini', $options["ini_{$this->_database}"]);
+        } else {
+            $this->debug("generateForeignKeys: SKIP - schema_location or ini_{database} was not set");
+            return null;
+        }
+
+
+        if (!file_exists(dirname($file))) {
+            mkdir(dirname($file), 0755, true);
+        }
+
+        $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');
+        if (!$fh) {
+            return (new PEAR)->raiseError(
+                "Failed to create temporary file: $tmpname\n" .
+                "make sure session.save_path is set and is writable\n",
+                null,
+                PEAR_ERROR_DIE
+            );
+        }
+        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);
+        return null;
     }
-    
-    
-    
-    
+
+
     /*
      * building the class files
      * for each of the tables output a file!
      */
+
     public function generateClasses()
     {
         //echo "Generating Class files:        \n";
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
-       
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
+
         $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
         $this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
-     
+
 
         foreach ($this->tables as $this->table) {
-            $this->table        = trim($this->table);
-            $this->classname    = $this->getClassNameFromTableName($this->table);
+            $this->table = trim($this->table);
+            $this->classname = $this->getClassNameFromTableName($this->table);
             $i = '';
-            $outfilename        = $this->getFileNameFromTableName($this->table);
-            
+            $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");
             if (!$fh) {
-                return PEAR::raiseError(
-                    "Failed to create temporary file: $tmpname\n".
+                return (new PEAR)->raiseError(
+                    "Failed to create temporary file: $tmpname\n" .
                     "make sure session.save_path is set and is writable\n",
                     null,
                     PEAR_ERROR_DIE
@@ -914,47 +874,72 @@ class DB_DataObject_Generator extends DB_DataObject
             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;
+        return null;
     }
 
     /**
-     * class being extended (can be overridden by [DB_DataObject] extends=xxxx
+     * Convert a table name into a class name -> override this if you want a different mapping
      *
-     * @var    string
-     * @access private
+     * @access  public
+     * @param $table
+     * @return  string class name;
      */
-    public $_extends = 'DB_DataObject';
 
-    /**
-     * line to use for require('DB/DataObject.php');
-     *
-     * @var    string
-     * @access private
-     */
-    public $_extendsFile = "DB/DataObject.php";
+
+    public function getClassNameFromTableName($table)
+    {
+        $options = &(new 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)));
+    }
 
     /**
-     * class being generated
+     * Convert a table name into a file name -> override this if you want a different mapping
      *
-     * @var    string
-     * @access private
+     * @access  public
+     * @param $table
+     * @return  string file name;
      */
-    public $_className;
+
+
+    public function getFileNameFromTableName($table)
+    {
+        $options = &(new 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';
+            (new 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;
+    }
 
     /**
      * The table class geneation part - single file.
      *
      * @access  private
-     * @return  none
+     * @param string $input
+     * @return none|string
      */
     public function _generateClassTable($input = '')
     {
@@ -965,10 +950,10 @@ class DB_DataObject_Generator extends DB_DataObject
         $head .= " */\n";
         $head .= $this->derivedHookExtendsDocBlock();
 
-        
+
         // requires - if you set extends_location = (blank) then no require line will be set
         // this can be used if you have an autoloader
-        
+
         if (!empty($this->_extendsFile)) {
             $head .= "require_once '{$this->_extendsFile}';\n\n";
         }
@@ -977,41 +962,41 @@ class DB_DataObject_Generator extends DB_DataObject
         $head .= $this->derivedHookClassDocBlock();
         $head .= "class {$this->classname} extends {$this->_extends} \n{";
 
-        $body =  "\n    ###START_AUTOCODE\n";
+        $body = "\n    ###START_AUTOCODE\n";
         $body .= "    /* the code below is auto generated do not remove the above tag */\n\n";
         // table
 
-        $p = str_repeat(' ', max(2, (18 - strlen($this->table)))) ;
-        
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
-        
-        
+        $p = str_repeat(' ', max(2, (18 - strlen($this->table))));
+
+        $options = &(new 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 (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {
             DB_DataObject::_loadConfig();
         }
 
         // Only include the $_database property if the omit_database_var is unset or false
-        
+
         if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
             $p = str_repeat(' ', max(2, (16 - strlen($this->_database))));
             $body .= "    {$var} \$_database = '{$this->_database}';  {$p}// database name (used with database_{*} config)\n";
         }
-        
-        
+
+
         if (!empty($options['generator_novars'])) {
-            $var = '//'.$var;
+            $var = '//' . $var;
         }
-        
+
         $defs = $this->_definitions[$this->table];
 
         // show nice information!
@@ -1023,28 +1008,28 @@ class DB_DataObject_Generator extends DB_DataObject
                 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";
+                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;
             }
-            
+
             $pad = str_repeat(' ', max(2, (30 - strlen($t->name))));
 
-            $length = empty($t->len) ? '' : '('.$t->len.')';
-            $flags = strlen($t->flags) ? (' '. trim($t->flags)) : '';
-            $body .="    {$var} \${$t->name}; {$pad}// {$t->type}{$length}{$flags}\n";
-            
+            $length = empty($t->len) ? '' : '(' . $t->len . ')';
+            $flags = strlen($t->flags) ? (' ' . trim($t->flags)) : '';
+            $body .= "    {$var} \${$t->name}; {$pad}// {$t->type}{$length}{$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, strlen($p));
         }
-         
+
         $body .= $this->derivedHookPostVar($defs);
 
         // THIS IS TOTALLY BORKED old FC creation
@@ -1058,16 +1043,16 @@ class DB_DataObject_Generator extends DB_DataObject
             $body .= "    /* ZE2 compatibility trick*/\n";
             $body .= "    function __clone() { return \$this;}\n";
         }
-        
-        
+
+
         // depricated - in here for BC...
         if (!empty($options['static_get'])) {
-            
+
             // simple creation tools ! (static stuff!)
             $body .= "\n";
             $body .= "    /* Static get */\n";
             $body .= "    $static  function staticGet(\$k,\$v=NULL) { " .
-                    "return DB_DataObject::staticGet('{$this->classname}',\$k,\$v = null); }\n";
+                "return DB_DataObject::staticGet('{$this->classname}',\$k,\$v = null); }\n";
         }
         // generate getter and setter methods
         $body .= $this->_generateGetters($input);
@@ -1083,7 +1068,7 @@ class DB_DataObject_Generator extends DB_DataObject
         //    $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']);
@@ -1103,7 +1088,7 @@ class DB_DataObject_Generator extends DB_DataObject
 
 
         // stubs..
-        
+
         if (!empty($options['generator_add_validate_stubs'])) {
             foreach ($defs as $t) {
                 if (!strlen(trim($t->name))) {
@@ -1119,8 +1104,6 @@ class DB_DataObject_Generator extends DB_DataObject
         }
 
 
-
-
         $foot .= "}\n";
         $full = $head . $body . $foot;
 
@@ -1139,7 +1122,7 @@ class DB_DataObject_Generator extends DB_DataObject
             unless use set generator_class_rewrite to ANY or a name*/
 
         $class_rewrite = 'DB_DataObject';
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
         if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
             $class_rewrite = 'DB_DataObject';
         }
@@ -1148,97 +1131,49 @@ class DB_DataObject_Generator extends DB_DataObject
         }
 
         $input = preg_replace(
-            '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
+            '/(\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(
+        $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".
+            return (new 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.
+     * hook to add extra page-level (in terms of phpDocumentor) DocBlock
      *
-     * @access   public
-     * @return  string added to class eg. functions.
+     * called once for each class, use it add extra page-level docs
+     * @access public
+     * @return string added to class eg. functions.
      */
-    public function derivedHookFunctions($input = "")
+    public function derivedHookPageLevelDocBlock()
     {
-        // This is so derived generator classes can generate functions
-        // It MUST NOT be changed here!!!
-        return "";
+        return '';
     }
 
     /**
-     * hook for var lines
-     * called each time a var line is generated, override to add extra var
-     * lines
+     * hook to add extra doc block (in terms of phpDocumentor) to extend string
      *
-     * @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.
-     */
-    public function derivedHookVar(&$t, $padding)
-    {
-        // This is so derived generator classes can generate variabels
-        // It MUST NOT be changed here!!!
-        return "";
-    }
-    /**
-     * hook for after var lines (
-     * called at the end of the output of var line have generated, override to add extra var
-     * lines
-     *
-     * @param array cols containing array of objects with type,len,flags etc. from tableInfo call
-     * @access   public
-     * @return  string added to class eg. functions.
-     */
-    public function derivedHookPostVar($t)
-    {
-        // 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.
-     */
-    public 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.
+     * called once for each class, use it add extra comments to extends
+     * string (require_once...)
+     * @access public
+     * @return string added to class eg. functions.
      */
     public function derivedHookExtendsDocBlock()
     {
@@ -1259,144 +1194,48 @@ class DB_DataObject_Generator extends DB_DataObject
     }
 
     /**
-
-    /**
-    * 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
-    */
-    public function getProxyFull($database, $table)
+     * 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.
+     */
+    public function derivedHookVar(&$t, $padding)
     {
-        if ($err = $this->fillTableSchema($database, $table)) {
-            return $err;
-        }
-        
-        
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
-        $class_prefix  = empty($options['class_prefix']) ? '' : $options['class_prefix'];
-        
-        $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
-        $this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
-        $classname = $this->classname = $this->getClassNameFromTableName($this->table);
-        
-        $out = $this->_generateClassTable();
-        //echo $out;
-        eval('?>'.$out);
-        return new $classname;
+        // This is so derived generator classes can generate variabels
+        // It MUST NOT be changed here!!!
+        return "";
     }
-    
+
     /**
-    * 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
-    */
-    public function fillTableSchema($database, $table)
+     * hook for after var lines (
+     * called at the end of the output of var line have generated, override to add extra var
+     * lines
+     *
+     * @param array cols containing array of objects with type,len,flags etc. from tableInfo call
+     * @access   public
+     * @return  string added to class eg. functions.
+     */
+    public function derivedHookPostVar($t)
     {
-        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_tableinfo']) ?
-                $__DB->quoteIdentifier($table) : $table;
-          
-        if (!$is_MDB2) {
-            $defs =  $__DB->tableInfo($quotedTable);
-        } else {
-            $defs =  $__DB->reverse->tableInfo($quotedTable);
-            if (PEAR::isError($defs)) {
-                return $defs;
-            }
-            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;
+        // This is so derived generator classes can generate variabels
+        // It MUST NOT be changed here!!!
+        return "";
     }
-    
+
     /**
-    * Generate getter methods for class definition
-    *
-    * @param    string  $input  Existing class contents
-    * @return   string
-    * @access   public
-    */
+     * Generate getter methods for class definition
+     *
+     * @param string $input Existing class contents
+     * @return   string
+     * @access   public
+     */
     public function _generateGetters($input)
     {
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
         $getters = '';
 
         // only generate if option is set to true
@@ -1408,10 +1247,10 @@ class DB_DataObject_Generator extends DB_DataObject
         $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];
+        $defs = $this->_definitions[$this->table];
 
         // loop through properties and create getter methods
-        foreach ($defs = $defs as $t) {
+        foreach ($defs as $t) {
 
             // build mehtod name
             $methodName = 'get' . $this->getMethodNameFromColumnName($t->name);
@@ -1424,101 +1263,112 @@ class DB_DataObject_Generator extends DB_DataObject
             $getters .= "    * Getter for \${$t->name}\n";
             $getters .= "    *\n";
             $getters .= (stristr($t->flags, 'multiple_key')) ? "    * @return   object\n"
-                                                             : "    * @return   {$t->type}\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 link setter/getter methods for class definition
-    *
-    * @param    string  Existing class contents
-    * @return   string
-    * @access   public
-    */
-    public function _generateLinkMethods($input)
+     * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
+     *
+     * @access  public
+     * @param $col
+     * @return  string method name;
+     */
+
+
+    public function getMethodNameFromColumnName($col)
     {
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+        return ucfirst($col);
+    }
+
+    /**
+     * Generate setter methods for class definition
+     *
+     * @param string  Existing class contents
+     * @return   string
+     * @access   public
+     */
+    public function _generateSetters($input)
+    {
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
         $setters = '';
 
         // only generate if option is set to true
-        
-        // generate_link_methods true::
-        
-        
-        if (empty($options['generate_link_methods'])) {
-            //echo "skip lm? - not set";
-            return '';
-        }
-        
-        if (empty($this->_fkeys)) {
-            // echo "skip lm? - fkyes empty";
-            return '';
-        }
-        if (empty($this->_fkeys[$this->table])) {
-            //echo "skip lm? - no fkeys for {$this->table}";
+        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->_fkeys[$this->table];
-         
-        
-        // $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
+        $defs = $this->_definitions[$this->table];
 
         // loop through properties and create setter methods
-        foreach ($defs as $k => $info) {
+        foreach ($defs as $t) {
 
             // build mehtod name
-            $methodName =  is_callable($options['generate_link_methods']) ?
-                    $options['generate_link_methods']($k) : $k;
+            $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
 
-            if (!strlen(trim($k)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
+            if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
                 continue;
             }
 
             $setters .= "   /**\n";
-            $setters .= "    * Getter / Setter for \${$k}\n";
+            $setters .= "    * Setter for \${$t->name}\n";
             $setters .= "    *\n";
-            $setters .= "    * @param    mixed   (optional) value to assign\n";
+            $setters .= "    * @param    mixed   input value\n";
             $setters .= "    * @access   public\n";
-            
             $setters .= "    */\n";
             $setters .= (substr(phpversion(), 0, 1) > 4) ? '    public '
-                                                       : '    ';
-            $setters .= "function $methodName() {\n";
-            $setters .= "        return \$this->link('$k', func_get_args());\n";
+                : '    ';
+            $setters .= "function $methodName(\$value) {\n";
+            $setters .= "        \$this->{$t->name} = \$value;\n";
             $setters .= "    }\n\n";
         }
-         
+
+
         return $setters;
     }
 
     /**
-     * Generate setter methods for class definition
+     * Generate link setter/getter methods for class definition
      *
-     * @param    string  Existing class contents
+     * @param string  Existing class contents
      * @return   string
      * @access   public
      */
-    public function _generateSetters($input)
+    public function _generateLinkMethods($input)
     {
-        $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
         $setters = '';
 
         // only generate if option is set to true
-        if (empty($options['generate_setters'])) {
+
+        // generate_link_methods true::
+
+
+        if (empty($options['generate_link_methods'])) {
+            //echo "skip lm? - not set";
+            return '';
+        }
+
+        if (empty($this->_fkeys)) {
+            // echo "skip lm? - fkyes empty";
+            return '';
+        }
+        if (empty($this->_fkeys[$this->table])) {
+            //echo "skip lm? - no fkeys for {$this->table}";
             return '';
         }
 
@@ -1526,51 +1376,56 @@ class DB_DataObject_Generator extends DB_DataObject
         $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];
+        $defs = $this->_fkeys[$this->table];
+
+
+        // $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
 
         // loop through properties and create setter methods
-        foreach ($defs = $defs as $t) {
+        foreach ($defs as $k => $info) {
 
             // build mehtod name
-            $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
+            $methodName = is_callable($options['generate_link_methods']) ?
+                $options['generate_link_methods']($k) : $k;
 
-            if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
+            if (!strlen(trim($k)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
                 continue;
             }
 
             $setters .= "   /**\n";
-            $setters .= "    * Setter for \${$t->name}\n";
+            $setters .= "    * Getter / Setter for \${$k}\n";
             $setters .= "    *\n";
-            $setters .= "    * @param    mixed   input value\n";
+            $setters .= "    * @param    mixed   (optional) value to assign\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 .= "function $methodName() {\n";
+            $setters .= "        return \$this->link('$k', func_get_args());\n";
             $setters .= "    }\n\n";
         }
-        
 
         return $setters;
     }
+
     /**
-    * Generate table Function - used when generator_no_ini is set.
-    *
-    * @param    array  table array.
-    * @return   string
-    * @access   public
-    */
+     * Generate table Function - used when generator_no_ini is set.
+     *
+     * @param array  table array.
+     * @return   string
+     * @access   public
+     */
     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) {
+            "    function table()\n" .
+            "    {\n" .
+            "         return array(\n";
+
+        foreach ($def as $k => $v) {
             $str = '0';
             foreach ($defines as $dn) {
                 if ($v & constant('DB_DATAOBJECT_' . $dn)) {
@@ -1581,126 +1436,126 @@ class DB_DataObject_Generator extends DB_DataObject
                 $str = substr($str, 3); // strip the 0 +
             }
             // hopefully addslashes is good enough here!!!
-            $ret .= '             \''.addslashes($k).'\' => ' . $str . ",\n";
+            $ret .= '             \'' . addslashes($k) . '\' => ' . $str . ",\n";
         }
         return $ret . "         );\n" .
-                      "    }\n";
+            "    }\n";
     }
+
     /**
-    * Generate keys Function - used generator_no_ini is set.
-    *
-    * @param    array  keys array.
-    * @return   string
-    * @access   public
-    */
+     * Generate keys Function - used generator_no_ini is set.
+     *
+     * @param array  keys array.
+     * @return   string
+     * @access   public
+     */
     public function _generateKeysFunction($def)
     {
         $ret = "\n" .
-               "    function keys()\n" .
-               "    {\n" .
-               "         return array(";
-            
-        foreach ($def as $k=>$type) {
+            "    function keys()\n" .
+            "    {\n" .
+            "         return array(";
+
+        foreach ($def as $k => $type) {
             // hopefully addslashes is good enough here!!!
-            $ret .= '\''.addslashes($k).'\', ';
+            $ret .= '\'' . addslashes($k) . '\', ';
         }
         $ret = preg_replace('#, $#', '', $ret);
         return $ret . ");\n" .
-                      "    }\n";
+            "    }\n";
     }
+
     /**
-    * Generate sequenceKey Function - used generator_no_ini is set.
-    *
-    * @param    array  table and key definition.
-    * @return   string
-    * @access   public
-    */
+     * Generate sequenceKey Function - used generator_no_ini is set.
+     *
+     * @param array  table and key definition.
+     * @return   string
+     * @access   public
+     */
     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'];
-        
-         
+
+
+        $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);
+
+
+        $ar = array(false, false, false);
         if ($usekey !== false) {
-            if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
-                $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
+            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')) &&
+
+            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);
+                $ar = array($usekey, true, $seqname);
             } else {
                 // use generated sequence keys..
                 if ($table[$usekey] & DB_DATAOBJECT_INT) {
-                    $ar = array($usekey,false,$seqname);
+                    $ar = array($usekey, false, $seqname);
                 }
             }
         }
-    
-    
-      
-     
+
+
         $ret = "\n" .
-               "    function sequenceKey() // keyname, use native, native name\n" .
-               "    {\n" .
-               "         return array(";
+            "    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";
+            "    }\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
-    */
+     * Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
+     * Only supports mysql and mysqli ... welcome ideas for more..
+     *
+     *
+     * @param $table
+     * @param $defs
+     * @return   string
+     * @access   public
+     */
     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.
+        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+        if (!in_array($__DB->phptype, array('mysql', 'mysqli'))) {
+            return null; // cant handle non-mysql introspection for defaults.
         }
-        $options = PEAR::getStaticProperty('DB_DataObject', 'options');
+        $options = (new 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);
@@ -1708,52 +1563,198 @@ class DB_DataObject_Generator extends DB_DataObject
         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';
+                    $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'];
+                    $defaults[$ar['Field']] = (int)(boolean)$ar['Default'];
                     break;
-                    
-                
+
+
                 case ($type & DB_DATAOBJECT_STR):
-                    $defaults[$ar['Field']] =  "'" . addslashes($ar['Default']) . "'";
+                    $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'];
+                        $defaults[$ar['Field']] = $ar['Default'];
                     }
                     break;
-            
+
             }
             //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
         }
         if (empty($defaults)) {
-            return;
+            return null;
         }
-        
+
         $ret = "\n" .
-               "    function defaults() // column default values \n" .
-               "    {\n" .
-               "         return array(\n";
-        foreach ($defaults as $k=>$v) {
-            $ret .= '             \''.addslashes($k).'\' => ' . $v . ",\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";
+            "    }\n";
+    }
+
+    /**
+     * 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
+     * @param string $input
+     * @return  string added to class eg. functions.
+     */
+    public function derivedHookFunctions($input = "")
+    {
+        // This is so derived generator classes can generate functions
+        // It MUST NOT be changed here!!!
+        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
+     */
+    public function getProxyFull($database, $table)
+    {
+        if ($err = $this->fillTableSchema($database, $table)) {
+            return $err;
+        }
+
+
+        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
+        $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
+
+        $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
+        $this->_extendsFile = !isset($options['extends_location']) ? $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|object|PEAR
+     * @access   public
+     */
+    public function fillTableSchema($database, $table)
+    {
+        global $_DB_DATAOBJECT;
+        // a little bit of sanity testing.
+        if ((false !== strpos($database, "'")) || (false !== strpos($database, ";"))) {
+            return (new 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 (new PEAR)->raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
+        }
+        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+
+
+        $options = (new 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_tableinfo']) ?
+            $__DB->quoteIdentifier($table) : $table;
+
+        if (!$is_MDB2) {
+            $defs = $__DB->tableInfo($quotedTable);
+        } else {
+            $defs = $__DB->reverse->tableInfo($quotedTable);
+            if ((new PEAR)->isError($defs)) {
+                return $defs;
+            }
+            foreach ($defs as $k => $v) {
+                if (!isset($defs[$k]['length'])) {
+                    continue;
+                }
+                $defs[$k]['len'] = $defs[$k]['length'];
+            }
+        }
+
+        if ((new 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;
     }
 }
index 92a7000621df525de8c48a673af587956b33dae4..eb44d67b02ca2ab4d647d896275516bf17eed03d 100644 (file)
@@ -29,8 +29,8 @@
  * Currenly only supports existing methods, and new 'link()' method
  *
  */
-  
-  
+
+
 /**
  * Links class
  *
 class DB_DataObject_Links
 {
     /**
-    * @property {DB_DataObject}      do   DataObject to apply this to.
-    */
+     * @property {DB_DataObject}      do   DataObject to apply this to.
+     */
     public $do = false;
-    
-    
+
+
     /**
      * @property {Array|String} load    What to load, 'all' or an array of properties. (default all)
      */
@@ -67,117 +67,102 @@ class DB_DataObject_Links
      * @property {Boolean}      apply   apply the result to this object, (default true)
      */
     public $apply = true;
-   
-    
+
+
     //------------------------- RETURN ------------------------------------
     /**
      * @property {Array}      links    key value associative array of links.
      */
     public $links;
-    
-    
+
+
     /**
      * Constructor
      *   -- good ole style..
-     *  @param {DB_DataObject}           do  DataObject to apply to.
-     *  @param {Array}           cfg  Configuration (basically properties of this object)
+     * @param {DB_DataObject}           do  DataObject to apply to.
+     * @param array $cfg
      */
-    
-    public function DB_DataObject_Links($do, $cfg= array())
+
+    public function __construct($do, $cfg = array())
     {
         // check if do is set!!!?
         $this->do = $do;
-        
-        foreach ($cfg as $k=>$v) {
+
+        foreach ($cfg as $k => $v) {
             $this->$k = $v;
         }
     }
-     
+
     /**
-     * return name from related object
+     *  a generic geter/setter provider..
      *
-     * The relies on  a <dbname>.links.ini file, unless you specify the arguments.
+     *  provides a generic getter setter for the referenced object
+     *  eg.
+     *  $link->link('company_id') returns getLink for the object
+     *  if nothing is linked (it will return an empty dataObject)
+     *  $link->link('company_id', array(1)) - just sets the
      *
-     * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
+     *  also array as the field speck supports
+     *      $link->link(array('company_id', 'company:id'))
      *
      *
-     * @param string $field|array    either row or row.xxxxx or links spec.
-     * @param string|DB_DataObject $table  (optional) name of table to look up value in
-     * @param string $link   (optional)  name of column in other table to match
-     * @author Tim White <tim@cyface.com>
-     * @access public
-     * @return mixed object on success false on failure or '0' when not linked
+     * @param string|array $field the field to fetch or link spec.
+     * @param array $args
+     * @return mixed true of false on set, the object on getter.
+     * @params array          $args    the arguments sent to the getter setter
      */
-    public function getLink($field, $table= false, $link='')
+    public function link($field, $args = array())
     {
-        static $cache = array();
-        
-        // GUESS THE LINKED TABLE.. (if found - recursevly call self)
-        
-        if ($table == false) {
-            $info = $this->linkInfo($field);
-            
-            if ($info) {
-                return $this->getLink($field, $info[0], $link === false ? $info[1] : $link);
-            }
-            
-            // no links defined.. - use borked BC method...
-            // use the old _ method - this shouldnt happen if called via getLinks()
-            if (!($p = strpos($field, '_'))) {
-                return false;
-            }
-            $table = substr($field, 0, $p);
-            return $this->getLink($field, $table);
-        }
-         
-        $tn = is_string($table) ? $table : $table->tableName();
-         
-            
-        if (!isset($this->do->$field)) {
-            $this->do->raiseError("getLink: row not set $field", DB_DATAOBJECT_ERROR_NODATA);
-            return false;
-        }
-        
-        // check to see if we know anything about this table..
-        
-      
-        if (empty($this->do->$field) || $this->do->$field < 0) {
-            return 0; // no record.
-        }
-        
-        if ($this->cached && isset($cache[$tn.':'. $link .':'. $this->do->$field])) {
-            return $cache[$tn.':'. $link .':'. $this->do->$field];
-        }
-        
-        $obj = is_string($table) ? $this->do->factory($tn) : $table;
-        ;
-        
-        if (!is_a($obj, 'DB_DataObject')) {
+        $info = $this->linkInfo($field);
+
+        if (!$info) {
             $this->do->raiseError(
-                "getLink:Could not find class for row $field, table $tn",
+                "getLink:Could not find link for row $field",
                 DB_DATAOBJECT_ERROR_INVALIDCONFIG
             );
             return false;
         }
-        // -1 or 0 -- no referenced record..
-       
-        $ret = false;
-        if ($link) {
-            if ($obj->get($link, $this->do->$field)) {
-                $ret = $obj;
+        $field = $info[2];
+
+
+        if (empty($args)) { // either an empty array or really empty....
+
+            if (!isset($this->do->$field)) {
+                return $info[0]; // empty dataobject.
             }
-            
-            
-            // this really only happens when no link config is set (old BC stuff)
-        } elseif ($obj->get($this->do->$field)) {
-            $ret= $obj;
+
+            $ret = $this->getLink($field);
+            // nothing linked -- return new object..
+            return ($ret === 0) ? $info[0] : $ret;
         }
-        if ($this->cached) {
-            $cache[$tn.':'. $link .':'. $this->do->$field] = $ret;
+        $assign = is_array($args) ? $args[0] : $args;
+
+        // otherwise it's a set call..
+        if (!is_a($assign, 'DB_DataObject')) {
+            if (is_numeric($assign) && is_integer($assign * 1)) {
+                if ($assign > 0) {
+                    if (!$info) {
+                        return false;
+                    }
+                    // check that record exists..
+                    if (!$info[0]->get($info[1], $assign)) {
+                        return false;
+                    }
+                }
+
+                $this->do->$field = $assign;
+                return true;
+            }
+
+            return false;
         }
-        return $ret;
+
+        // otherwise we are assigning it ...
+
+        $this->do->$field = $assign->{$info[1]};
+        return true;
     }
+
     /**
      * get link information for a field or field specification
      *
@@ -187,11 +172,10 @@ class DB_DataObject_Links
      * array(3) : 'field', $dataobject, 'remote_col'  (handy for joinAdd to do nested joins.)
      *
      * @param string|array $field or link spec to use.
-     * @return (false|array) array of dataobject and linked field or false.
-     *
+     * @return array|bool (false|array) array of dataobject and linked field or false.
      *
      */
-    
+
     public function linkInfo($field)
     {
         if (is_array($field)) {
@@ -205,7 +189,7 @@ class DB_DataObject_Links
                 );
             }
             list($table, $link) = explode(':', $field[1]);
-            
+
             return array(
                 $this->do->factory($table),
                 $link,
@@ -213,103 +197,116 @@ class DB_DataObject_Links
             );
         }
         // work out the link.. (classic way)
-        
+
         $links = $this->do->links();
-        
+
         if (empty($links) || !is_array($links)) {
             return false;
         }
-            
-            
+
+
         if (!isset($links[$field])) {
             return false;
         }
         list($table, $link) = explode(':', $links[$field]);
-    
-        
+
+
         //??? needed???
         if ($p = strpos($field, ".")) {
             $field = substr($field, 0, $p);
         }
-        
+
         return array(
             $this->do->factory($table),
             $link,
             $field
         );
     }
-    
-    
-        
+
     /**
-     *  a generic geter/setter provider..
-     *
-     *  provides a generic getter setter for the referenced object
-     *  eg.
-     *  $link->link('company_id') returns getLink for the object
-     *  if nothing is linked (it will return an empty dataObject)
-     *  $link->link('company_id', array(1)) - just sets the
+     * return name from related object
      *
-     *  also array as the field speck supports
-     *      $link->link(array('company_id', 'company:id'))
+     * The relies on  a <dbname>.links.ini file, unless you specify the arguments.
      *
+     * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
      *
-     *  @param  string|array   $field   the field to fetch or link spec.
-     *  @params array          $args    the arguments sent to the getter setter
-     *  @return mixed true of false on set, the object on getter.
      *
+     * @param string $field |array    either row or row.xxxxx or links spec.
+     * @param bool $table (optional) name of table to look up value in
+     * @param string $link (optional)  name of column in other table to match
+     * @return mixed object on success false on failure or '0' when not linked
+     * @author Tim White <tim@cyface.com>
+     * @access public
      */
-    public function link($field, $args = array())
+    public function getLink($field, $table = false, $link = '')
     {
-        $info = $this->linkInfo($field);
-         
-        if (!$info) {
+        static $cache = array();
+
+        // GUESS THE LINKED TABLE.. (if found - recursevly call self)
+
+        if ($table == false) {
+            $info = $this->linkInfo($field);
+
+            if ($info) {
+                return $this->getLink($field, $info[0], $link === false ? $info[1] : $link);
+            }
+
+            // no links defined.. - use borked BC method...
+            // use the old _ method - this shouldnt happen if called via getLinks()
+            if (!($p = strpos($field, '_'))) {
+                return false;
+            }
+            $table = substr($field, 0, $p);
+            return $this->getLink($field, $table);
+        }
+
+        $tn = is_string($table) ? $table : $table->tableName();
+
+
+        if (!isset($this->do->$field)) {
+            $this->do->raiseError("getLink: row not set $field", DB_DATAOBJECT_ERROR_NODATA);
+            return false;
+        }
+
+        // check to see if we know anything about this table..
+
+
+        if (empty($this->do->$field) || $this->do->$field < 0) {
+            return 0; // no record.
+        }
+
+        if ($this->cached && isset($cache[$tn . ':' . $link . ':' . $this->do->$field])) {
+            return $cache[$tn . ':' . $link . ':' . $this->do->$field];
+        }
+
+        $obj = is_string($table) ? $this->do->factory($tn) : $table;;
+
+        if (!is_a($obj, 'DB_DataObject')) {
             $this->do->raiseError(
-                "getLink:Could not find link for row $field",
+                "getLink:Could not find class for row $field, table $tn",
                 DB_DATAOBJECT_ERROR_INVALIDCONFIG
             );
             return false;
         }
-        $field = $info[2];
-        
-        
-        if (empty($args)) { // either an empty array or really empty....
-            
-            if (!isset($this->do->$field)) {
-                return $info[0]; // empty dataobject.
+        // -1 or 0 -- no referenced record..
+
+        $ret = false;
+        if ($link) {
+            if ($obj->get($link, $this->do->$field)) {
+                $ret = $obj;
             }
-            
-            $ret = $this->getLink($field);
-            // nothing linked -- return new object..
-            return ($ret === 0) ? $info[0] : $ret;
+
+
+            // this really only happens when no link config is set (old BC stuff)
+        } elseif ($obj->get($this->do->$field)) {
+            $ret = $obj;
         }
-        $assign = is_array($args) ? $args[0] : $args;
-         
-        // otherwise it's a set call..
-        if (!is_a($assign, 'DB_DataObject')) {
-            if (is_numeric($assign) && is_integer($assign * 1)) {
-                if ($assign  > 0) {
-                    if (!$info) {
-                        return false;
-                    }
-                    // check that record exists..
-                    if (!$info[0]->get($info[1], $assign)) {
-                        return false;
-                    }
-                }
-                
-                $this->do->$field = $assign ;
-                return true;
-            }
-            
-            return false;
+        if ($this->cached) {
+            $cache[$tn . ':' . $link . ':' . $this->do->$field] = $ret;
         }
-        
-        // otherwise we are assigning it ...
-        
-        $this->do->$field = $assign->{$info[1]};
-        return true;
+        return $ret;
     }
+
     /**
      * load related objects
      *
@@ -322,26 +319,26 @@ class DB_DataObject_Links
      * object vars the links are stored in by  changeing the format parameter
      *
      *
-     * @param  string format (default _%s) where %s is the table name.
+     * @param string format (default _%s) where %s is the table name.
+     * @return boolean , true on success
      * @author Tim White <tim@cyface.com>
      * @access public
-     * @return boolean , true on success
      */
-    
+
     public function applyLinks($format = '_%s')
     {
-         
+
         // get table will load the options.
         if ($this->do->_link_loaded) {
             return true;
         }
-        
+
         $this->do->_link_loaded = false;
-        $cols  = $this->do->table();
+        $cols = $this->do->table();
         $links = $this->do->links();
-         
+
         $loaded = array();
-        
+
         if ($links) {
             foreach ($links as $key => $match) {
                 list($table, $link) = explode(':', $match);
@@ -350,9 +347,9 @@ class DB_DataObject_Links
                 if ($p = strpos($key, '.')) {
                     $key = substr($key, 0, $p);
                 }
-                
+
                 $this->do->$k = $this->getLink($key, $table, $link);
-                
+
                 if (is_object($this->do->$k)) {
                     $loaded[] = $k;
                 }
@@ -367,14 +364,14 @@ class DB_DataObject_Links
         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);
+            $k = sprintf($format, $key);
             $this->do->$k = $this->getLink($key);
             if (is_object($this->do->$k)) {
                 $loaded[] = $k;
@@ -383,7 +380,7 @@ class DB_DataObject_Links
         $this->do->_link_loaded = $loaded;
         return true;
     }
-    
+
     /**
      * getLinkArray
      * Fetch an array of related objects. This should be used in conjunction with a
@@ -392,11 +389,11 @@ class DB_DataObject_Links
      * You may also use this with all parameters to specify, the column and related table.
      *
      * @access public
-     * @param string $field- either column or column.xxxxx
+     * @param string $field - either column or column.xxxxx
      * @param string $table (optional) name of table to look up value in
-     * @param string $fkey (optional) fetchall key see DB_DataObject::fetchAll()
-     * @param string $fval (optional)fetchall val DB_DataObject::fetchAll()
-     * @param string $fval (optional) fetchall method DB_DataObject::fetchAll()
+     * @param bool $fkey (optional) fetchall key see DB_DataObject::fetchAll()
+     * @param bool $fval (optional) fetchall method DB_DataObject::fetchAll()
+     * @param bool $fmethod
      * @return array - array of results (empty array on failure)
      *
      * Example - Getting the related objects
@@ -409,14 +406,13 @@ class DB_DataObject_Links
      * foreach ($children as $child) {
      *     echo $child->name, '<br />';
      * }
-     *
      */
     public function getLinkArray($field, $table = null, $fkey = false, $fval = false, $fmethod = false)
     {
         $ret = array();
         if (!$table) {
             $links = $this->do->links();
-            
+
             if (is_array($links)) {
                 if (!isset($links[$field])) {
                     // failed..
@@ -430,9 +426,9 @@ class DB_DataObject_Links
             }
             return $this->getLinkArray($field, substr($field, 0, $p));
         }
-        
-        $c  = $this->do->factory($table);
-        
+
+        $c = $this->do->factory($table);
+
         if (!is_object($c) || !is_a($c, 'DB_DataObject')) {
             $this->do->raiseError(
                 "getLinkArray:Could not find class for row $field, table $table",
index b6d3dedf8c000fc088540de2f5ad4dddae0ba52a..4645711665f28a227ea665d306f63026c1997f4b 100755 (executable)
 define('DB_DATAOBJECT_NO_OVERLOAD', 1);
 
 //require_once 'DB/DataObject/Generator.php';
-require_once 'DB/DataObject/Generator.php';
+require_once 'Generator.php';
 
 if (php_sapi_name() != 'cli') {
-    PEAR::raiseError("\nERROR: You must turn use the cli sapi to run this", null, PEAR_ERROR_DIE);
+    (new PEAR)->raiseError("\nERROR: You must turn use the cli sapi to run this", null, PEAR_ERROR_DIE);
 }
 
 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);
+    (new 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\n" .$_SERVER['argv'][0] . " example.ini\n\n", null, PEAR_ERROR_DIE);
+    (new PEAR)->raiseError("\nERROR: createTable.php usage:\n\n" . $_SERVER['argv'][0] . " 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');
+foreach ($config as $class => $values) {
+    $options = &(new PEAR)->getStaticProperty($class, 'options');
     $options = $values;
 }
 
 
-$options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+$options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
 if (empty($options)) {
-    PEAR::raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE);
+    (new PEAR)->raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE);
     exit;
 }
 set_time_limit(0);
index 22fac96d9910277970357b97931ed09761bbd8ee..7252ce27864bb7b448f16ef8b9d634c295cc47e2 100644 (file)
@@ -169,23 +169,23 @@ class DB_common extends PEAR
         }
         if (isset($this->autocommit)) {
             return array('autocommit',
-                         'dbsyntax',
-                         'dsn',
-                         'features',
-                         'fetchmode',
-                         'fetchmode_object_class',
-                         'options',
-                         'was_connected',
-                   );
+                'dbsyntax',
+                'dsn',
+                'features',
+                'fetchmode',
+                'fetchmode_object_class',
+                'options',
+                'was_connected',
+            );
         } else {
             return array('dbsyntax',
-                         'dsn',
-                         'features',
-                         'fetchmode',
-                         'fetchmode_object_class',
-                         'options',
-                         'was_connected',
-                   );
+                'dsn',
+                'features',
+                'fetchmode',
+                'fetchmode_object_class',
+                'options',
+                'was_connected',
+            );
         }
     }
 
@@ -212,37 +212,37 @@ class DB_common extends PEAR
     // {{{ __toString()
 
     /**
-     * Automatic string conversion for PHP 5
+     * DEPRECATED:  String conversion method
      *
      * @return string  a string describing the current PEAR DB object
      *
-     * @since Method available since Release 1.7.0
+     * @deprecated Method deprecated in Release 1.7.0
      */
-    public function __toString()
+    public function toString()
     {
-        $info = strtolower(get_class($this));
-        $info .=  ': (phptype=' . $this->phptype .
-                  ', dbsyntax=' . $this->dbsyntax .
-                  ')';
-        if ($this->connection) {
-            $info .= ' [connected]';
-        }
-        return $info;
+        return $this->__toString();
     }
 
     // }}}
     // {{{ toString()
 
     /**
-     * DEPRECATED:  String conversion method
+     * Automatic string conversion for PHP 5
      *
      * @return string  a string describing the current PEAR DB object
      *
-     * @deprecated Method deprecated in Release 1.7.0
+     * @since Method available since Release 1.7.0
      */
-    public function toString()
+    public function __toString()
     {
-        return $this->__toString();
+        $info = strtolower(get_class($this));
+        $info .= ': (phptype=' . $this->phptype .
+            ', dbsyntax=' . $this->dbsyntax .
+            ')';
+        if ($this->connection) {
+            $info .= ' [connected]';
+        }
+        return $info;
     }
 
     // }}}
@@ -252,7 +252,7 @@ class DB_common extends PEAR
      * DEPRECATED: Quotes a string so it can be safely used within string
      * delimiters in a query
      *
-     * @param string $string  the string to be quoted
+     * @param string $string the string to be quoted
      *
      * @return string  the quoted string
      *
@@ -271,77 +271,13 @@ class DB_common extends PEAR
     // }}}
     // {{{ 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
-     */
-    public function quote($string = null)
-    {
-        return $this->quoteSmart($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
-     */
-    public 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
+     * @param mixed $in the data to be formatted
      *
      * @return mixed  the formatted data.  The format depends on the input's
      *                 PHP type:
@@ -455,6 +391,46 @@ class DB_common extends PEAR
         }
     }
 
+    // }}}
+    // {{{ quoteIdentifier()
+
+    /**
+     * 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.
+     */
+    public function quoteFloat($float)
+    {
+        return "'" . $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))) . "'";
+    }
+
+    // }}}
+    // {{{ quoteSmart()
+
+    /**
+     * 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
+     */
+    public function escapeSimple($str)
+    {
+        return str_replace("'", "''", $str);
+    }
+
     // }}}
     // {{{ quoteBoolean()
 
@@ -471,45 +447,69 @@ class DB_common extends PEAR
     {
         return $boolean ? '1' : '0';
     }
-     
+
     // }}}
     // {{{ quoteFloat()
 
     /**
-     * Formats a float value for use within a query in a locale-independent
-     * manner.
+     * DEPRECATED: Quotes a string so it can be safely used in a query
      *
-     * @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.
+     * @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
      */
-    public function quoteFloat($float)
+    public function quote($string = null)
     {
-        return "'".$this->escapeSimple(str_replace(',', '.', strval(floatval($float))))."'";
+        return $this->quoteSmart($string);
     }
-     
+
     // }}}
     // {{{ escapeSimple()
 
     /**
-     * Escapes a string according to the current DBMS's standards
+     * Quotes a string so it can be safely used as a table or column name
      *
-     * 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.
+     * Delimiting style depends on which database driver is being used.
      *
-     * @param string $str  the string to be escaped
+     * 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.
      *
-     * @return string  the escaped string
+     * 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
      *
-     * @see DB_common::quoteSmart()
      * @since Method available since Release 1.6.0
      */
-    public function escapeSimple($str)
+    public function quoteIdentifier($str)
     {
-        return str_replace("'", "''", $str);
+        return '"' . str_replace('"', '""', $str) . '"';
     }
 
     // }}}
@@ -518,7 +518,7 @@ class DB_common extends PEAR
     /**
      * Tells whether the present driver supports a given feature
      *
-     * @param string $feature  the feature you're curious about
+     * @param string $feature the feature you're curious about
      *
      * @return bool  whether this driver supports $feature
      */
@@ -533,9 +533,9 @@ class DB_common extends PEAR
     /**
      * Sets the fetch mode that should be used by default for query results
      *
-     * @param integer $fetchmode    DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC
+     * @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
+     * @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
@@ -543,6 +543,7 @@ class DB_common extends PEAR
      *                               done.  There is also the posibility to use
      *                               and extend the 'DB_row' class.
      *
+     * @return object
      * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
      */
     public function setFetchMode($fetchmode, $object_class = 'stdClass')
@@ -550,7 +551,7 @@ class DB_common extends PEAR
         switch ($fetchmode) {
             case DB_FETCHMODE_OBJECT:
                 $this->fetchmode_object_class = $object_class;
-                // no break
+            // no break
             case DB_FETCHMODE_ORDERED:
             case DB_FETCHMODE_ASSOC:
                 $this->fetchmode = $fetchmode;
@@ -558,11 +559,95 @@ class DB_common extends PEAR
             default:
                 return $this->raiseError('invalid fetchmode mode');
         }
+        return null;
     }
 
     // }}}
     // {{{ setOption()
 
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ getOption()
+
     /**
      * Sets run-time configuration options for PEAR DB
      *
@@ -695,9 +780,9 @@ class DB_common extends PEAR
      * </code>
      *
      * @param string $option option name
-     * @param mixed  $value value for the option
+     * @param mixed $value value for the option
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_common::$options
      */
@@ -715,173 +800,40 @@ class DB_common extends PEAR
                     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
-     */
-    public 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()
-     */
-    public 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()
+                                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;
+                }
+            }
 
-    /**
-     * 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()
-     */
-    public 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 DB_OK;
         }
-        return $this->prepare($query);
+        return $this->raiseError("unknown option $option");
     }
 
     // }}}
-    // {{{ autoExecute()
+    // {{{ prepare()
 
     /**
      * 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
+     * @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:
+     * @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
+     * @param bool $where for update queries: the WHERE clause to
      *                               append to the SQL statement.  Don't
      *                               include the "WHERE" keyword.
      *
@@ -896,7 +848,8 @@ class DB_common extends PEAR
         $fields_values,
         $mode = DB_AUTOQUERY_INSERT,
         $where = false
-    ) {
+    )
+    {
         $sth = $this->autoPrepare(
             $table,
             array_keys($fields_values),
@@ -912,7 +865,39 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ buildManipSQL()
+    // {{{ 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 bool $where for update queries: the WHERE clause to
+     *                               append to the SQL statement.  Don't
+     *                               include the "WHERE" keyword.
+     *
+     * @return resource|string
+     *
+     * @uses DB_common::prepare(), DB_common::buildManipSQL()
+     */
+    public 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()
 
     /**
      * Produces an SQL query string for autoPrepare()
@@ -934,11 +919,11 @@ class DB_common extends PEAR
      *   - 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:
+     * @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
+     * @param bool $where for update queries: the WHERE clause to
      *                               append to the SQL statement.  Don't
      *                               include the "WHERE" keyword.
      *
@@ -985,6 +970,90 @@ class DB_common extends PEAR
         }
     }
 
+    // }}}
+    // {{{ buildManipSQL()
+
+    /**
+     * 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()
+     */
+    public 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;
+    }
+
     // }}}
     // {{{ execute()
 
@@ -1002,8 +1071,8 @@ class DB_common extends PEAR
      * $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
+     * @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
@@ -1039,8 +1108,8 @@ class DB_common extends PEAR
     /**
      * 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
+     * @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
@@ -1089,6 +1158,33 @@ class DB_common extends PEAR
     // }}}
     // {{{ executeMultiple()
 
+    /**
+     * 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()
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ freePrepared()
+
     /**
      * Performs several execute() calls on the same statement handle
      *
@@ -1098,8 +1194,8 @@ class DB_common extends PEAR
      * 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
+     * @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.
@@ -1117,33 +1213,6 @@ class DB_common extends PEAR
         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()
-     */
-    public 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()
 
@@ -1152,7 +1221,7 @@ class DB_common extends PEAR
      *
      * It is defined here to ensure all drivers have this method available.
      *
-     * @param string $query  the query string to modify
+     * @param string $query the query string to modify
      *
      * @return string  the modified query string
      *
@@ -1168,16 +1237,49 @@ class DB_common extends PEAR
     // }}}
     // {{{ modifyLimitQuery()
 
+    /**
+     * 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.
+     */
+    public 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_object($result) && is_a($result, 'DB_result')) {
+            $result->setOption('limit_from', $from);
+            $result->setOption('limit_count', $count);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ query()
+
     /**
      * 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
+     * @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
@@ -1193,7 +1295,7 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ query()
+    // {{{ limitQuery()
 
     /**
      * Sends a query to the database server
@@ -1202,8 +1304,8 @@ class DB_common extends PEAR
      * 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
+     * @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
@@ -1231,43 +1333,10 @@ class DB_common extends PEAR
             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.
-     */
-    public 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_object($result) && is_a($result, 'DB_result')) {
-            $result->setOption('limit_from', $from);
-            $result->setOption('limit_count', $count);
+                $tmp = new DB_result($this, $result);
+                return $tmp;
+            }
         }
-        return $result;
     }
 
     // }}}
@@ -1278,8 +1347,8 @@ class DB_common extends PEAR
      *
      * 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
+     * @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
@@ -1325,13 +1394,13 @@ class DB_common extends PEAR
      *
      * 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
+     * @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
+     * @param int $fetchmode the fetch mode to use
      *
      * @return array  the first row of results as an array.
      *                 A DB_Error object on failure.
@@ -1340,7 +1409,8 @@ class DB_common extends PEAR
         $query,
         $params = array(),
         $fetchmode = DB_FETCHMODE_DEFAULT
-    ) {
+    )
+    {
         // compat check, the params and fetchmode parameters used to
         // have the opposite order
         if (!is_array($params)) {
@@ -1387,70 +1457,6 @@ class DB_common extends PEAR
     // }}}
     // {{{ 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()
-     */
-    public 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
@@ -1513,26 +1519,26 @@ class DB_common extends PEAR
      * 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
+     * @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
+     * @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
+     * @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.
+     * @return array|object
      *                A DB_Error object on failure.
      */
     public function &getAssoc(
@@ -1541,7 +1547,8 @@ class DB_common extends PEAR
         $params = array(),
         $fetchmode = DB_FETCHMODE_DEFAULT,
         $group = false
-    ) {
+    )
+    {
         $params = (array)$params;
         if (sizeof($params) > 0) {
             $sth = $this->prepare($query);
@@ -1630,31 +1637,32 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ getAll()
+    // {{{ getAssoc()
 
     /**
      * 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
+     * @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:
+     * @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.
+     * @return array|object
      */
     public 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)) {
@@ -1710,14 +1718,14 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ getAll()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @param bool $onoff true turns it on, false turns it off
      *
-     * @return int  DB_OK on success.  A DB_Error object if the driver
+     * @return int|object
      *               doesn't support auto-committing transactions.
      */
     public function autoCommit($onoff = false)
@@ -1726,12 +1734,12 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ autoCommit()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -1739,12 +1747,12 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ commit()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -1752,14 +1760,14 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ rollback()
 
     /**
      * Determines the number of rows in a query result
      *
-     * @param resource $result  the query result idenifier produced by PHP
+     * @param resource $result the query result idenifier produced by PHP
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      */
     public function numRows($result)
     {
@@ -1767,14 +1775,14 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ numRows()
 
     /**
      * 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.
+     * @return int|object
      */
     public function affectedRows()
     {
@@ -1782,7 +1790,7 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ getSequenceName()
+    // {{{ affectedRows()
 
     /**
      * Generates the name used inside the database for a sequence
@@ -1790,7 +1798,7 @@ class DB_common extends PEAR
      * The createSequence() docblock contains notes about storing sequence
      * names.
      *
-     * @param string $sqn  the sequence's public name
+     * @param string $sqn the sequence's public name
      *
      * @return string  the sequence's name in the backend
      *
@@ -1806,17 +1814,35 @@ class DB_common extends PEAR
         );
     }
 
+    // }}}
+    // {{{ getSequenceName()
+
+    /**
+     * Returns the value of an option
+     *
+     * @param string $option the option name you're curious about
+     *
+     * @return mixed  the option's value
+     */
+    public function getOption($option)
+    {
+        if (isset($this->options[$option])) {
+            return $this->options[$option];
+        }
+        return $this->raiseError("unknown option $option");
+    }
+
     // }}}
     // {{{ 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::createSequence(), DB_common::dropSequence(),
@@ -1840,9 +1866,9 @@ class DB_common extends PEAR
      *
      * <var>seqname_format</var> is set via setOption().
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
      *      DB_common::nextID()
@@ -1858,9 +1884,9 @@ class DB_common extends PEAR
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_common::createSequence(), DB_common::getSequenceName(),
      *      DB_common::nextID()
@@ -1873,88 +1899,6 @@ class DB_common extends PEAR
     // }}}
     // {{{ 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
-     */
-    public 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
      *
@@ -1966,14 +1910,14 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ errorCode()
+    // {{{ errorNative()
 
     /**
      * 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
+     * @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
@@ -1989,12 +1933,12 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ errorMessage()
+    // {{{ errorCode()
 
     /**
      * Maps a DB error code to a textual message
      *
-     * @param integer $dbcode  the DB error code
+     * @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.
@@ -2007,7 +1951,7 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ tableInfo()
+    // {{{ errorMessage()
 
     /**
      * Returns information about a table or a result set
@@ -2112,19 +2056,19 @@ class DB_common extends PEAR
      * 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
+     * @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:
+     * @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.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::setOption()
@@ -2140,7 +2084,7 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ getTables()
+    // {{{ tableInfo()
 
     /**
      * Lists the tables in the current database
@@ -2155,17 +2099,17 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ getListOf()
+    // {{{ getTables()
 
     /**
      * Lists internal database information
      *
-     * @param string $type  type of information being sought.
+     * @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.
+     * @return array|object
      *                 A DB DB_Error object on failure.
      */
     public function getListOf($type)
@@ -2186,12 +2130,12 @@ class DB_common extends PEAR
     }
 
     // }}}
-    // {{{ getSpecialQuery()
+    // {{{ getListOf()
 
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -2204,6 +2148,70 @@ class DB_common extends PEAR
         return $this->raiseError(DB_ERROR_UNSUPPORTED);
     }
 
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * 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()
+     */
+    public 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;
+    }
+
     // }}}
     // {{{ nextQueryIsManip()
 
@@ -2247,7 +2255,6 @@ class DB_common extends PEAR
         }
         $this->_next_query_manip = false;
         return $this->_last_query_manip;
-        $manip = $this->_next_query_manip;
     }
 
     // }}}
@@ -2256,7 +2263,7 @@ class DB_common extends PEAR
     /**
      * Right-trims all strings in an array
      *
-     * @param array $array  the array to be trimmed (passed by reference)
+     * @param array $array the array to be trimmed (passed by reference)
      *
      * @return void
      *
@@ -2277,7 +2284,7 @@ class DB_common extends PEAR
     /**
      * Converts all null values in an array to empty strings
      *
-     * @param array  $array  the array to be de-nullified (passed by reference)
+     * @param array $array the array to be de-nullified (passed by reference)
      *
      * @return void
      *
index d500ea5afdfd6bd3d8b2700fbdecfd8d9c33b097..f72540d8941fbd76de2e374f420aef27353527af 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's dbase extension
@@ -74,21 +75,20 @@ class DB_dbase extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => false,
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => false,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => false,
+        '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
      */
-    public $errorcode_map = array(
-    );
+    public $errorcode_map = array();
 
     /**
      * The raw database connection created by PHP
@@ -189,15 +189,15 @@ class DB_dbase extends DB_common
      * );
      *
      * $db = DB::connect($dsn, $options);
-     * if (PEAR::isError($db)) {
+     * if ((new PEAR)->isError($db)) {
      *     die($db->getMessage());
      * }
      * </code>
      *
-     * @param array $dsn         the data source name
-     * @param bool  $persistent  should the connection be persistent?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -226,9 +226,9 @@ class DB_dbase extends DB_common
                     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'
+                    . 'it could not be created because '
+                    . 'the "fields" element of the DSN '
+                    . 'is not properly set'
                 );
             }
             $this->connection = @dbase_create(
@@ -242,8 +242,8 @@ class DB_dbase extends DB_common
                     null,
                     null,
                     'the dbase file does not exist and '
-                                         . 'the attempt to create it failed: '
-                                         . $php_errormsg
+                    . 'the attempt to create it failed: '
+                    . $php_errormsg
                 );
             }
         } else {
@@ -306,10 +306,10 @@ class DB_dbase extends DB_common
      * 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)
+     * @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
@@ -350,7 +350,7 @@ class DB_dbase extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -371,8 +371,7 @@ class DB_dbase extends DB_common
      * 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
-     *
+     * @param $foo
      * @return int  the number of columns.  A DB_Error object on failure.
      *
      * @see DB_result::numCols()
@@ -392,8 +391,7 @@ class DB_dbase extends DB_common
      * 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
-     *
+     * @param $foo
      * @return int  the number of rows.  A DB_Error object on failure.
      *
      * @see DB_result::numRows()
@@ -419,18 +417,18 @@ class DB_dbase extends DB_common
     {
         return $boolean ? 'T' : 'F';
     }
-     
+
     // }}}
     // {{{ tableInfo()
 
     /**
      * Returns information about the current database
      *
-     * @param mixed $result  THIS IS UNUSED IN DBASE.  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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -467,7 +465,7 @@ class DB_dbase extends DB_common
             }
 
             $id = array();
-            $i  = 0;
+            $i = 0;
 
             $line = fread($db, 32);
             while (!feof($db)) {
@@ -478,8 +476,8 @@ class DB_dbase extends DB_common
                     $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)],
+                        'name' => substr($line, 0, $pos),
+                        'type' => $this->types[substr($line, 11, 1)],
                         'length' => ord(substr($line, 16, 1)),
                         'precision' => ord(substr($line, 17, 1)),
                     );
@@ -496,7 +494,7 @@ class DB_dbase extends DB_common
             $case_func = 'strval';
         }
 
-        $res   = array();
+        $res = array();
         $count = count($id);
 
         if ($mode) {
@@ -506,9 +504,9 @@ class DB_dbase extends DB_common
         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'],
+                'name' => $case_func($id[$i]['name']),
+                'type' => $id[$i]['type'],
+                'len' => $id[$i]['length'],
                 'flags' => ''
             );
             if ($mode & DB_TABLEINFO_ORDER) {
index d3579fe82f842b8895aa9e7155dc98106648a46b..3c1c3f6651ee3184d98e72c70e455eb47ef16829 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's fbsql extension
@@ -75,13 +76,13 @@ class DB_fbsql extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'alter',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => true,
+        'limit' => 'alter',
+        'new_link' => false,
+        'numrows' => true,
+        'pconnect' => true,
+        'prepare' => false,
+        'ssl' => false,
+        'transactions' => true,
     );
 
     /**
@@ -89,8 +90,8 @@ class DB_fbsql extends DB_common
      * @var array
      */
     public $errorcode_map = array(
-         22 => DB_ERROR_SYNTAX,
-         85 => DB_ERROR_ALREADY_EXISTS,
+        22 => DB_ERROR_SYNTAX,
+        85 => DB_ERROR_ALREADY_EXISTS,
         108 => DB_ERROR_SYNTAX,
         116 => DB_ERROR_NOSUCHTABLE,
         124 => DB_ERROR_VALUE_COUNT_ON_ROW,
@@ -141,10 +142,10 @@ class DB_fbsql extends DB_common
      *
      * 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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -203,6 +204,35 @@ class DB_fbsql extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * 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()
+     */
+    public 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)
+        );
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
     /**
      * Disconnects from the database server
      *
@@ -216,7 +246,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ nextResult()
 
     /**
      * Sends a query to the database server
@@ -244,7 +274,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ nextResult()
+    // {{{ fetchInto()
 
     /**
      * Move the internal fbsql result pointer to the next available result
@@ -261,7 +291,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ freeResult()
 
     /**
      * Places a row from the result set into the given array
@@ -273,10 +303,10 @@ class DB_fbsql extends DB_common
      * 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)
+     * @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
@@ -311,7 +341,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ autoCommit()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -320,7 +350,7 @@ class DB_fbsql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -332,27 +362,28 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ commit()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
      */
-    public function autoCommit($onoff=false)
+    public function autoCommit($onoff = false)
     {
         if ($onoff) {
             $this->query("SET COMMIT TRUE");
         } else {
             $this->query("SET COMMIT FALSE");
         }
+        return null;
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ rollback()
 
     /**
      * Commits the current transaction
@@ -362,10 +393,11 @@ class DB_fbsql extends DB_common
     public function commit()
     {
         @fbsql_commit($this->connection);
+        return 0;
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ numCols()
 
     /**
      * Reverts the current transaction
@@ -375,10 +407,11 @@ class DB_fbsql extends DB_common
     public function rollback()
     {
         @fbsql_rollback($this->connection);
+        return 0;
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ numRows()
 
     /**
      * Gets the number of columns in a result set
@@ -387,9 +420,9 @@ class DB_fbsql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -403,7 +436,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ affectedRows()
 
     /**
      * Gets the number of rows in a result set
@@ -412,9 +445,9 @@ class DB_fbsql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -428,7 +461,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ nextId()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -447,17 +480,14 @@ class DB_fbsql extends DB_common
         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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -489,10 +519,13 @@ class DB_fbsql extends DB_common
         return $tmp[0];
     }
 
+    // }}}
+    // {{{ dropSequence()
+
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -503,8 +536,8 @@ class DB_fbsql extends DB_common
     {
         $seqname = $this->getSequenceName($seq_name);
         $res = $this->query('CREATE TABLE ' . $seqname
-                            . ' (id INTEGER NOT NULL,'
-                            . ' PRIMARY KEY(id))');
+            . ' (id INTEGER NOT NULL,'
+            . ' PRIMARY KEY(id))');
         if ($res) {
             $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname);
         }
@@ -512,12 +545,12 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ modifyLimitQuery()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -527,19 +560,19 @@ class DB_fbsql extends DB_common
     public function dropSequence($seq_name)
     {
         return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
-                            . ' RESTRICT');
+            . ' RESTRICT');
     }
 
     // }}}
-    // {{{ modifyLimitQuery()
+    // {{{ quoteBoolean()
 
     /**
      * 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
+     * @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
@@ -567,7 +600,7 @@ class DB_fbsql extends DB_common
     }
 
     // }}}
-    // {{{ quoteBoolean()
+    // {{{ quoteFloat()
 
     /**
      * Formats a boolean value for use within a query in a locale-independent
@@ -582,9 +615,9 @@ class DB_fbsql extends DB_common
     {
         return $boolean ? 'TRUE' : 'FALSE';
     }
-     
+
     // }}}
-    // {{{ quoteFloat()
+    // {{{ fbsqlRaiseError()
 
     /**
      * Formats a float value for use within a query in a locale-independent
@@ -599,35 +632,6 @@ class DB_fbsql extends DB_common
     {
         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()
-     */
-    public 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()
@@ -648,14 +652,14 @@ class DB_fbsql extends DB_common
     /**
      * Returns information about a table or a result set
      *
-     * @param object|string  $result  DB_result object from a query or a
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -701,7 +705,7 @@ class DB_fbsql extends DB_common
         }
 
         $count = @fbsql_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -710,9 +714,9 @@ class DB_fbsql extends DB_common
         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),
+                '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) {
@@ -736,7 +740,7 @@ class DB_fbsql extends DB_common
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -749,32 +753,32 @@ class DB_fbsql extends DB_common
         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';
+                    . ' 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';
+                    . ' 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';
+                    . ' 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';
+                    . ' 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;
         }
index 940462a592fed4d7381ab0488cd4990fd1d0a868..a91bd35772ee4fec04b99974be65a698dde34af4 100644 (file)
@@ -30,7 +30,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's interbase extension
@@ -85,13 +86,13 @@ class DB_ibase extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => false,
-        'new_link'      => false,
-        'numrows'       => 'emulate',
-        'pconnect'      => true,
-        'prepare'       => true,
-        'ssl'           => false,
-        'transactions'  => true,
+        'limit' => false,
+        'new_link' => false,
+        'numrows' => 'emulate',
+        'pconnect' => true,
+        'prepare' => true,
+        'ssl' => false,
+        'transactions' => true,
     );
 
     /**
@@ -207,10 +208,10 @@ class DB_ibase extends DB_common
      *                 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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -228,14 +229,14 @@ class DB_ibase extends DB_common
 
         $params = array(
             $dsn['hostspec']
-                    ? ($dsn['hostspec'] . ':' . $dsn['database'])
-                    : $dsn['database'],
+                ? ($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,
+            isset($dsn['role']) ? $dsn['role'] : null,
         );
 
         $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
@@ -250,6 +251,112 @@ class DB_ibase extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * 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()
+     */
+    public function &ibaseRaiseError($errno = null)
+    {
+        if ($errno === null) {
+            $errno = $this->errorCode($this->errorNative());
+        }
+        $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
+        return $tmp;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * 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
+     */
+    public 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()
+                '/violation of [\w ]+ constraint/i'
+                => DB_ERROR_CONSTRAINT,
+                '/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,
+                '/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;
+    }
+
+    // }}}
+    // {{{ modifyLimitQuery()
+
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
     /**
      * Disconnects from the database server
      *
@@ -263,7 +370,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ fetchInto()
 
     /**
      * Sends a query to the database server
@@ -297,17 +404,17 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ modifyLimitQuery()
+    // {{{ freeResult()
 
     /**
      * 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
+     * @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
@@ -330,7 +437,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ nextResult()
+    // {{{ freeQuery()
 
     /**
      * Move the internal ibase result pointer to the next available result
@@ -347,7 +454,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ affectedRows()
 
     /**
      * Places a row from the result set into the given array
@@ -359,10 +466,10 @@ class DB_ibase extends DB_common
      * 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)
+     * @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
@@ -399,7 +506,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ numCols()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -408,7 +515,7 @@ class DB_ibase extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -420,7 +527,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ freeQuery()
+    // {{{ prepare()
 
     public function freeQuery($query)
     {
@@ -428,14 +535,14 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ execute()
 
     /**
      * 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.
+     * @return int|object
      */
     public function affectedRows()
     {
@@ -445,9 +552,6 @@ class DB_ibase extends DB_common
         return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
     }
 
-    // }}}
-    // {{{ numCols()
-
     /**
      * Gets the number of columns in a result set
      *
@@ -455,9 +559,9 @@ class DB_ibase extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -471,7 +575,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ prepare()
+    // {{{ autoCommit()
 
     /**
      * Prepares a query for multiple execution with execute().
@@ -497,14 +601,14 @@ class DB_ibase extends DB_common
      */
     public function prepare($query)
     {
-        $tokens   = preg_split(
+        $tokens = preg_split(
             '/((?<!\\\)[&?!])/',
             $query,
             -1,
             PREG_SPLIT_DELIM_CAPTURE
         );
-        $token    = 0;
-        $types    = array();
+        $token = 0;
+        $types = array();
         $newquery = '';
 
         foreach ($tokens as $key => $val) {
@@ -527,26 +631,26 @@ class DB_ibase extends DB_common
         $newquery = substr($newquery, 0, -1);
         $this->last_query = $query;
         $newquery = $this->modifyQuery($newquery);
-        $stmt = @ibase_prepare($this->connection, $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);
+            $this->manip_query[(int)$stmt] = DB::isManip($query);
         }
 
         return $stmt;
     }
 
     // }}}
-    // {{{ execute()
+    // {{{ commit()
 
     /**
      * 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
+     * @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
@@ -613,11 +717,14 @@ class DB_ibase extends DB_common
         return $tmp;
     }
 
+    // }}}
+    // {{{ rollback()
+
     /**
      * 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?
+     * @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.
      *
@@ -640,12 +747,12 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ transactionInit()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -657,7 +764,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ nextId()
 
     /**
      * Commits the current transaction
@@ -670,7 +777,7 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ createSequence()
 
     /**
      * Reverts the current transaction
@@ -683,26 +790,26 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ transactionInit()
+    // {{{ dropSequence()
 
     public function transactionInit($trans_args = 0)
     {
         return $trans_args
-                ? @ibase_trans($trans_args, $this->connection)
-                : @ibase_trans();
+            ? @ibase_trans($trans_args, $this->connection)
+            : @ibase_trans();
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ _ibaseFieldFlags()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -715,8 +822,8 @@ class DB_ibase extends DB_common
         do {
             $this->pushErrorHandling(PEAR_ERROR_RETURN);
             $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
-                                   . 'FROM RDB$GENERATORS '
-                                   . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
+                . 'FROM RDB$GENERATORS '
+                . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
             $this->popErrorHandling();
             if ($ondemand && DB::isError($result)) {
                 $repeat = 1;
@@ -737,12 +844,12 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ createSequence()
+    // {{{ ibaseRaiseError()
 
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -760,12 +867,12 @@ class DB_ibase extends DB_common
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ errorNative()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -775,206 +882,28 @@ class DB_ibase extends DB_common
     public 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
-     */
-    public 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()
-     */
-    public 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
-     */
-    public 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;
+            . "WHERE RDB\$GENERATOR_NAME='"
+            . strtoupper($this->getSequenceName($seq_name))
+            . "'");
     }
 
     // }}}
     // {{{ 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
-     */
-    public 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()
-                '/violation of [\w ]+ constraint/i'
-                    => DB_ERROR_CONSTRAINT,
-                '/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,
-                '/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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -1019,7 +948,7 @@ class DB_ibase extends DB_common
         }
 
         $count = @ibase_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -1029,12 +958,12 @@ class DB_ibase extends DB_common
             $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'],
+                'name' => $case_func($info['name']),
+                'type' => $info['type'],
+                'len' => $info['length'],
                 'flags' => ($got_string)
-                            ? $this->_ibaseFieldFlags($info['name'], $result)
-                            : '',
+                    ? $this->_ibaseFieldFlags($info['name'], $result)
+                    : '',
             );
             if ($mode & DB_TABLEINFO_ORDER) {
                 $res['order'][$res[$i]['name']] = $i;
@@ -1051,13 +980,85 @@ class DB_ibase extends DB_common
         return $res;
     }
 
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * 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
+     */
+    public 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);
+    }
+
     // }}}
     // {{{ 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 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
@@ -1070,7 +1071,7 @@ class DB_ibase extends DB_common
         switch ($type) {
             case 'tables':
                 return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
-                       . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
+                    . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
             case 'views':
                 return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
             case 'users':
index 87e5d48b9d07e354b9c9e4144195be1225387315..d398dd26939ca538896abecd23976ccec1c0152d 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's ifx extension
@@ -81,13 +82,13 @@ class DB_ifx extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'emulate',
-        'new_link'      => false,
-        'numrows'       => 'emulate',
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => true,
+        'limit' => 'emulate',
+        'new_link' => false,
+        'numrows' => 'emulate',
+        'pconnect' => true,
+        'prepare' => false,
+        'ssl' => false,
+        'transactions' => true,
     );
 
     /**
@@ -95,33 +96,33 @@ class DB_ifx extends DB_common
      * @var array
      */
     public $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,
+        '-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,
     );
 
     /**
@@ -184,10 +185,10 @@ class DB_ifx extends DB_common
      *
      * 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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -218,6 +219,72 @@ class DB_ifx extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * 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()
+     */
+    public function ifxRaiseError($errno = null)
+    {
+        if ($errno === null) {
+            $errno = $this->errorCode(ifx_error());
+        }
+        return $this->raiseError(
+            $errno,
+            null,
+            null,
+            null,
+            $this->errorNative()
+        );
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * 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.
+     */
+    public function errorCode($nativecode)
+    {
+        if (preg_match('/SQLCODE=(.*)]/', $nativecode, $match)) {
+            $code = $match[1];
+            if (isset($this->errorcode_map[$code])) {
+                return $this->errorcode_map[$code];
+            }
+        }
+        return DB_ERROR;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Gets the DBMS' native error code and message produced by the last query
+     *
+     * @return string  the DBMS' error code and message
+     */
+    public function errorNative()
+    {
+        return @ifx_error() . ' ' . @ifx_errormsg();
+    }
+
+    // }}}
+    // {{{ affectedRows()
+
     /**
      * Disconnects from the database server
      *
@@ -231,7 +298,7 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ fetchInto()
 
     /**
      * Sends a query to the database server
@@ -246,7 +313,7 @@ class DB_ifx extends DB_common
     {
         $ismanip = $this->_checkManip($query);
         $this->last_query = $query;
-        $this->affected   = null;
+        $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
@@ -282,7 +349,7 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ nextResult()
+    // {{{ numCols()
 
     /**
      * Move the internal ifx result pointer to the next available result
@@ -299,7 +366,7 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ freeResult()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -318,7 +385,7 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ autoCommit()
 
     /**
      * Places a row from the result set into the given array
@@ -330,10 +397,10 @@ class DB_ifx extends DB_common
      * 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)
+     * @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
@@ -359,14 +426,14 @@ class DB_ifx extends DB_common
             return null;
         }
         if ($fetchmode !== DB_FETCHMODE_ASSOC) {
-            $i=0;
+            $i = 0;
             $order = array();
             foreach ($arr as $val) {
                 $order[$i++] = $val;
             }
             $arr = $order;
         } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
-                  $this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
             $arr = array_change_key_case($arr, CASE_LOWER);
         }
         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
@@ -379,7 +446,7 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ commit()
 
     /**
      * Gets the number of columns in a result set
@@ -388,9 +455,9 @@ class DB_ifx extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -403,7 +470,7 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ rollback()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -412,7 +479,7 @@ class DB_ifx extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -424,12 +491,12 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ ifxRaiseError()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -443,12 +510,12 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ errorNative()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -463,12 +530,12 @@ class DB_ifx extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ errorCode()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -482,72 +549,6 @@ class DB_ifx extends DB_common
         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()
-     */
-    public 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
-     */
-    public 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.
-     */
-    public function errorCode($nativecode)
-    {
-        if (preg_match('/SQLCODE=(.*)]/', $nativecode, $match)) {
-            $code = $match[1];
-            if (isset($this->errorcode_map[$code])) {
-                return $this->errorcode_map[$code];
-            }
-        }
-        return DB_ERROR;
-    }
-
     // }}}
     // {{{ tableInfo()
 
@@ -560,14 +561,14 @@ class DB_ifx extends DB_common
      * 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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -618,7 +619,7 @@ class DB_ifx extends DB_common
             $case_func = 'strval';
         }
 
-        $i   = 0;
+        $i = 0;
         $res = array();
 
         if ($mode) {
@@ -629,9 +630,9 @@ class DB_ifx extends DB_common
             $props = explode(';', $value);
             $res[$i] = array(
                 'table' => $got_string ? $case_func($result) : '',
-                'name'  => $case_func($key),
-                'type'  => $props[0],
-                'len'   => $props[1],
+                'name' => $case_func($key),
+                'type' => $props[0],
+                'len' => $props[1],
                 'flags' => $props[4] == 'N' ? 'not_null' : '',
             );
             if ($mode & DB_TABLEINFO_ORDER) {
@@ -656,7 +657,7 @@ class DB_ifx extends DB_common
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
index 399215bc369832a7ce76ef237a3d458abad1b9f3..b583b5c635452bef81b2e3ea4f394cea5aabd50b 100644 (file)
@@ -30,7 +30,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's msql extension
@@ -81,21 +82,20 @@ class DB_msql extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'emulate',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => false,
+        '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
      */
-    public $errorcode_map = array(
-    );
+    public $errorcode_map = array();
 
     /**
      * The raw database connection created by PHP
@@ -154,15 +154,15 @@ class DB_msql extends DB_common
      * );
      *
      * $db = DB::connect($dsn, $options);
-     * if (PEAR::isError($db)) {
+     * if ((new PEAR)->isError($db)) {
      *     die($db->getMessage());
      * }
      * </code>
      *
-     * @param array $dsn         the data source name
-     * @param bool  $persistent  should the connection be persistent?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -178,8 +178,8 @@ class DB_msql extends DB_common
         $params = array();
         if ($dsn['hostspec']) {
             $params[] = $dsn['port']
-                        ? $dsn['hostspec'] . ',' . $dsn['port']
-                        : $dsn['hostspec'];
+                ? $dsn['hostspec'] . ',' . $dsn['port']
+                : $dsn['hostspec'];
         }
 
         $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
@@ -229,6 +229,127 @@ class DB_msql extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * 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()
+     */
+    public function msqlRaiseError($errno = null)
+    {
+        $native = $this->errorNative();
+        if ($errno === null) {
+            $errno = $this->errorCode($native);
+        }
+        return $this->raiseError($errno, null, null, null, $native);
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Gets the DBMS' native error message produced by the last query
+     *
+     * @return string  the DBMS' error message
+     */
+    public function errorNative()
+    {
+        return @msql_error();
+    }
+
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
     /**
      * Disconnects from the database server
      *
@@ -242,7 +363,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ freeResult()
 
     /**
      * Sends a query to the database server
@@ -272,9 +393,8 @@ class DB_msql extends DB_common
         }
     }
 
-
     // }}}
-    // {{{ nextResult()
+    // {{{ numCols()
 
     /**
      * Move the internal msql result pointer to the next available result
@@ -291,7 +411,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ numRows()
 
     /**
      * Places a row from the result set into the given array
@@ -307,10 +427,10 @@ class DB_msql extends DB_common
      * 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)
+     * @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
@@ -345,7 +465,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ affected()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -354,7 +474,7 @@ class DB_msql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -366,7 +486,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ nextId()
 
     /**
      * Gets the number of columns in a result set
@@ -375,9 +495,9 @@ class DB_msql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -391,7 +511,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ createSequence()
 
     /**
      * Gets the number of rows in a result set
@@ -400,9 +520,9 @@ class DB_msql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -416,7 +536,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ affected()
+    // {{{ dropSequence()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -434,16 +554,16 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ quoteIdentifier()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -479,7 +599,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ createSequence()
+    // {{{ quoteFloat()
 
     /**
      * Creates a new sequence
@@ -487,7 +607,7 @@ class DB_msql extends DB_common
      * 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
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -498,7 +618,7 @@ class DB_msql extends DB_common
     {
         $seqname = $this->getSequenceName($seq_name);
         $res = $this->query('CREATE TABLE ' . $seqname
-                            . ' (id INTEGER NOT NULL)');
+            . ' (id INTEGER NOT NULL)');
         if (DB::isError($res)) {
             return $res;
         }
@@ -507,12 +627,12 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ escapeSimple()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -525,12 +645,12 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ quoteIdentifier()
+    // {{{ msqlRaiseError()
 
     /**
      * mSQL does not support delimited identifiers
      *
-     * @param string $str  the identifier name to be quoted
+     * @param string $str the identifier name to be quoted
      *
      * @return object  a DB_Error object
      *
@@ -543,7 +663,7 @@ class DB_msql extends DB_common
     }
 
     // }}}
-    // {{{ quoteFloat()
+    // {{{ errorNative()
 
     /**
      * Formats a float value for use within a query in a locale-independent
@@ -558,14 +678,14 @@ class DB_msql extends DB_common
     {
         return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
     }
-     
+
     // }}}
-    // {{{ escapeSimple()
+    // {{{ errorCode()
 
     /**
      * Escapes a string according to the current DBMS's standards
      *
-     * @param string $str  the string to be escaped
+     * @param string $str the string to be escaped
      *
      * @return string  the escaped string
      *
@@ -577,140 +697,20 @@ class DB_msql extends DB_common
         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()
-     */
-    public 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
-     */
-    public 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
-     */
-    public 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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::setOption()
@@ -755,7 +755,7 @@ class DB_msql extends DB_common
         }
 
         $count = @msql_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -775,9 +775,9 @@ class DB_msql extends DB_common
 
             $res[$i] = array(
                 'table' => $case_func($tmp->table),
-                'name'  => $case_func($tmp->name),
-                'type'  => $tmp->type,
-                'len'   => msql_field_len($id, $i),
+                'name' => $case_func($tmp->name),
+                'type' => $tmp->type,
+                'len' => msql_field_len($id, $i),
                 'flags' => $flags,
             );
 
@@ -802,9 +802,9 @@ class DB_msql extends DB_common
     /**
      * Obtain a list of a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @param string $type the kind of objects you want to retrieve
      *
-     * @return array  the array containing the list of objects requested
+     * @return array|object
      *
      * @access protected
      * @see DB_common::getListOf()
index 94a5a83af686ebc6f3d21f99242b3b5b57ea7f61..4a764100ef2d7f5c51d3c38d0028377ad80c5215 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's mssql extension
@@ -82,13 +83,13 @@ class DB_mssql extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'emulate',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => true,
+        'limit' => 'emulate',
+        'new_link' => false,
+        'numrows' => true,
+        'pconnect' => true,
+        'prepare' => false,
+        'ssl' => false,
+        'transactions' => true,
     );
 
     /**
@@ -97,39 +98,39 @@ class DB_mssql extends DB_common
      */
     // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
     public $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,
+        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,
     );
 
@@ -196,10 +197,10 @@ class DB_mssql extends DB_common
      *
      * 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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -220,7 +221,7 @@ class DB_mssql extends DB_common
         );
         if ($dsn['port']) {
             $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
-                        . $dsn['port'];
+                . $dsn['port'];
         }
 
         $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
@@ -307,6 +308,80 @@ class DB_mssql extends DB_common
     // }}}
     // {{{ nextResult()
 
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param null $code
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_mssql::errorNative(), DB_mssql::errorCode()
+     */
+    public 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"
+        );
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Gets the DBMS' native error code produced by the last query
+     *
+     * @return int  the DBMS' error code
+     */
+    public function errorNative()
+    {
+        $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
+        if (!$res) {
+            return DB_ERROR;
+        }
+        $row = @mssql_fetch_row($res);
+        return $row[0];
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * 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
+     * @param string $msg
+     * @return integer  an error number from a DB error constant
+     * @see errorNative()
+     */
+    public 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;
+        }
+    }
+
+    // }}}
+    // {{{ numCols()
+
     /**
      * Move the internal mssql result pointer to the next available result
      *
@@ -322,7 +397,7 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ numRows()
 
     /**
      * Places a row from the result set into the given array
@@ -334,10 +409,10 @@ class DB_mssql extends DB_common
      * 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)
+     * @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
@@ -372,7 +447,7 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ autoCommit()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -381,7 +456,7 @@ class DB_mssql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -393,7 +468,7 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ commit()
 
     /**
      * Gets the number of columns in a result set
@@ -402,9 +477,9 @@ class DB_mssql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -418,7 +493,7 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ rollback()
 
     /**
      * Gets the number of rows in a result set
@@ -427,9 +502,9 @@ class DB_mssql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -443,12 +518,12 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ affectedRows()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -462,12 +537,12 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ nextId()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -484,13 +559,10 @@ class DB_mssql extends DB_common
         return DB_OK;
     }
 
-    // }}}
-    // {{{ rollback()
-
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -508,14 +580,14 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ dropSequence()
 
     /**
      * 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.
+     * @return int|object
      */
     public function affectedRows()
     {
@@ -538,16 +610,16 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ escapeSimple()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -593,10 +665,13 @@ class DB_mssql extends DB_common
         return $result[0];
     }
 
+    // }}}
+    // {{{ quoteIdentifier()
+
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -606,18 +681,18 @@ class DB_mssql extends DB_common
     public function createSequence($seq_name)
     {
         return $this->query('CREATE TABLE '
-                            . $this->getSequenceName($seq_name)
-                            . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
-                            . ' [vapor] [int] NULL)');
+            . $this->getSequenceName($seq_name)
+            . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
+            . ' [vapor] [int] NULL)');
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ mssqlRaiseError()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -630,12 +705,12 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ escapeSimple()
+    // {{{ errorNative()
 
     /**
      * Escapes a string in a manner suitable for SQL Server.
      *
-     * @param string $str  the string to be escaped
+     * @param string $str the string to be escaped
      * @return string  the escaped string
      *
      * @see DB_common::quoteSmart()
@@ -651,12 +726,12 @@ class DB_mssql extends DB_common
     }
 
     // }}}
-    // {{{ quoteIdentifier()
+    // {{{ errorCode()
 
     /**
      * Quotes a string so it can be safely used as a table or column name
      *
-     * @param string $str  identifier name to be quoted
+     * @param string $str identifier name to be quoted
      *
      * @return string  quoted identifier string
      *
@@ -668,82 +743,6 @@ class DB_mssql extends DB_common
         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()
-     */
-    public 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
-     */
-    public 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()
-     */
-    public 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()
 
@@ -753,14 +752,14 @@ class DB_mssql extends DB_common
      * 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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -808,7 +807,7 @@ class DB_mssql extends DB_common
         }
 
         $count = @mssql_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -829,9 +828,9 @@ class DB_mssql extends DB_common
 
             $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),
+                '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) {
@@ -864,10 +863,10 @@ class DB_mssql extends DB_common
      * 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
+     * @param string $table the table name
+     * @param string $column the field name
      *
-     * @return string  the flags
+     * @return array|string
      *
      * @access private
      * @author Joern Barthel <j_barthel@web.de>
@@ -928,7 +927,7 @@ class DB_mssql extends DB_common
         }
 
         if (array_key_exists($column, $flags)) {
-            return(implode(' ', $flags[$column]));
+            return (implode(' ', $flags[$column]));
         }
         return '';
     }
@@ -940,8 +939,8 @@ class DB_mssql extends DB_common
      * 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
+     * @param array  &$array the reference to the flag-array
+     * @param string $value the flag value
      *
      * @return void
      *
@@ -963,7 +962,7 @@ class DB_mssql extends DB_common
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -976,7 +975,7 @@ class DB_mssql extends DB_common
         switch ($type) {
             case 'tables':
                 return "SELECT name FROM sysobjects WHERE type = 'U'"
-                       . ' ORDER BY name';
+                    . ' ORDER BY name';
             case 'views':
                 return "SELECT name FROM sysobjects WHERE type = 'V'";
             default:
index a541cb7a14231616054dea179d4aad7d934aab26..a992e28adec718ef5a317e370148df8a6ddb99ec 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's mysql extension
@@ -74,13 +75,13 @@ class DB_mysql extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'alter',
-        'new_link'      => '4.2.0',
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => true,
+        'limit' => 'alter',
+        'new_link' => '4.2.0',
+        'numrows' => true,
+        'pconnect' => true,
+        'prepare' => false,
+        'ssl' => false,
+        'transactions' => true,
     );
 
     /**
@@ -188,10 +189,10 @@ class DB_mysql extends DB_common
      *                    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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -209,7 +210,7 @@ class DB_mysql extends DB_common
             $params[0] = ':' . $dsn['socket'];
         } else {
             $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
-                         : 'localhost';
+                : 'localhost';
             if ($dsn['port']) {
                 $params[0] .= ':' . $dsn['port'];
             }
@@ -227,7 +228,7 @@ class DB_mysql extends DB_common
         }
         if (version_compare(phpversion(), '4.3.0', '>=')) {
             $params[] = isset($dsn['client_flags'])
-                        ? $dsn['client_flags'] : null;
+                ? $dsn['client_flags'] : null;
         }
 
         $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
@@ -281,6 +282,46 @@ class DB_mysql extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * 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()
+     */
+    public 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)
+        );
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
     /**
      * Disconnects from the database server
      *
@@ -294,7 +335,7 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ nextResult()
 
     /**
      * Sends a query to the database server
@@ -344,7 +385,40 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ nextResult()
+    // {{{ fetchInto()
+
+    /**
+     * 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()
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ freeResult()
 
     /**
      * Move the internal mysql result pointer to the next available result
@@ -361,7 +435,7 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ numCols()
 
     /**
      * Places a row from the result set into the given array
@@ -373,10 +447,10 @@ class DB_mysql extends DB_common
      * 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)
+     * @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
@@ -416,7 +490,7 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ numRows()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -425,7 +499,7 @@ class DB_mysql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -437,7 +511,7 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ autoCommit()
 
     /**
      * Gets the number of columns in a result set
@@ -446,9 +520,9 @@ class DB_mysql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -462,7 +536,7 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ commit()
 
     /**
      * Gets the number of rows in a result set
@@ -471,9 +545,9 @@ class DB_mysql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -487,12 +561,12 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ rollback()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -506,12 +580,12 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ affectedRows()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -532,12 +606,12 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ nextId()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -558,7 +632,7 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ createSequence()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -577,16 +651,16 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ dropSequence()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -598,8 +672,8 @@ class DB_mysql extends DB_common
         do {
             $repeat = 0;
             $this->pushErrorHandling(PEAR_ERROR_RETURN);
-            $result = $this->query("UPDATE ${seqname} ".
-                                   'SET id=LAST_INSERT_ID(id+1)');
+            $result = $this->query("UPDATE ${seqname} " .
+                'SET id=LAST_INSERT_ID(id+1)');
             $this->popErrorHandling();
             if ($result === DB_OK) {
                 // COMMON CASE
@@ -627,7 +701,7 @@ class DB_mysql extends DB_common
 
                 // Release the lock
                 $result = $this->getOne('SELECT RELEASE_LOCK('
-                                        . "'${seqname}_lock')");
+                    . "'${seqname}_lock')");
                 if (DB::isError($result)) {
                     return $this->raiseError($result);
                 }
@@ -643,7 +717,7 @@ class DB_mysql extends DB_common
                     $repeat = 1;
                 }
             } elseif (DB::isError($result) &&
-                      $result->getCode() == DB_ERROR_ALREADY_EXISTS) {
+                $result->getCode() == DB_ERROR_ALREADY_EXISTS) {
                 // BACKWARDS COMPAT
                 // see _BCsequence() comment
                 $result = $this->_BCsequence($seqname);
@@ -658,12 +732,12 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ createSequence()
+    // {{{ _BCsequence()
 
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -674,8 +748,8 @@ class DB_mysql extends DB_common
     {
         $seqname = $this->getSequenceName($seq_name);
         $res = $this->query('CREATE TABLE ' . $seqname
-                            . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
-                            . ' PRIMARY KEY(id))');
+            . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+            . ' PRIMARY KEY(id))');
         if (DB::isError($res)) {
             return $res;
         }
@@ -689,33 +763,15 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ 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()
-     */
-    public function dropSequence($seq_name)
-    {
-        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
-    }
-
-    // }}}
-    // {{{ _BCsequence()
+    // {{{ quoteIdentifier()
 
     /**
      * Backwards compatibility with old sequence emulation implementation
      * (clean up the dupes)
      *
-     * @param string $seqname  the sequence name to clean up
+     * @param string $seqname the sequence name to clean up
      *
-     * @return bool  true on success.  A DB_Error object on failure.
+     * @return bool|object
      *
      * @access private
      */
@@ -742,7 +798,7 @@ class DB_mysql extends DB_common
         // 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");
+            . " WHERE id <> $highest_id");
         if (DB::isError($result)) {
             return $result;
         }
@@ -758,7 +814,25 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ quoteIdentifier()
+    // {{{ escapeSimple()
+
+    /**
+     * 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()
+     */
+    public function dropSequence($seq_name)
+    {
+        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+    }
+
+    // }}}
+    // {{{ modifyQuery()
 
     /**
      * Quotes a string so it can be safely used as a table or column name
@@ -767,7 +841,7 @@ class DB_mysql extends DB_common
      * 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
+     * @param string $str identifier name to be quoted
      *
      * @return string  quoted identifier string
      *
@@ -780,12 +854,12 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ escapeSimple()
+    // {{{ modifyLimitQuery()
 
     /**
      * Escapes a string according to the current DBMS's standards
      *
-     * @param string $str  the string to be escaped
+     * @param string $str the string to be escaped
      *
      * @return string  the escaped string
      *
@@ -802,48 +876,15 @@ class DB_mysql extends DB_common
     }
 
     // }}}
-    // {{{ 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()
-     */
-    public 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()
+    // {{{ mysqlRaiseError()
 
     /**
      * 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
+     * @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
@@ -862,46 +903,6 @@ class DB_mysql extends DB_common
         }
     }
 
-    // }}}
-    // {{{ 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()
-     */
-    public 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()
 
@@ -921,14 +922,14 @@ class DB_mysql extends DB_common
     /**
      * Returns information about a table or a result set
      *
-     * @param object|string  $result  DB_result object from a query or a
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -942,7 +943,7 @@ class DB_mysql extends DB_common
                     return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
                 }
             }
-            
+
             /*
              * Probably received a table name.
              * Create a result resource identifier.
@@ -980,7 +981,7 @@ class DB_mysql extends DB_common
         }
 
         $count = @mysql_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -989,9 +990,9 @@ class DB_mysql extends DB_common
         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),
+                '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) {
@@ -1015,7 +1016,7 @@ class DB_mysql extends DB_common
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
index 42ba23e4f93c57aad0e319e74640758d64a97d0d..cc59c1157001fcbeb8e02bc7550da52bc4758359 100644 (file)
@@ -26,7 +26,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's mysqli extension
@@ -77,13 +78,13 @@ class DB_mysqli extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'alter',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => false,
-        'prepare'       => false,
-        'ssl'           => true,
-        'transactions'  => true,
+        'limit' => 'alter',
+        'new_link' => false,
+        'numrows' => true,
+        'pconnect' => false,
+        'prepare' => false,
+        'ssl' => true,
+        'transactions' => true,
     );
 
     /**
@@ -167,19 +168,19 @@ class DB_mysqli extends DB_common
      * @since  Property available since Release 1.6.5
      */
     public $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_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'
+        MYSQLI_GROUP_FLAG => 'group_by'
     );
 
     /**
@@ -189,34 +190,34 @@ class DB_mysqli extends DB_common
      * @since  Property available since Release 1.6.5
      */
     public $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_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_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',
+        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',
+        16 => 'bit',
+        246 => 'decimal',
     );
 
 
@@ -272,15 +273,15 @@ class DB_mysqli extends DB_common
      * );
      *
      * $db = DB::connect($dsn, $options);
-     * if (PEAR::isError($db)) {
+     * if ((new PEAR)->isError($db)) {
      *     die($db->getMessage());
      * }
      * </code>
      *
-     * @param array $dsn         the data source name
-     * @param bool  $persistent  should the connection be persistent?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -297,13 +298,13 @@ class DB_mysqli extends DB_common
         @ini_set('track_errors', 1);
         $php_errormsg = '';
 
-        if (((int) $this->getOption('ssl')) === 1) {
+        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['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']
             );
@@ -418,6 +419,46 @@ class DB_mysqli extends DB_common
     // }}}
     // {{{ nextResult()
 
+    /**
+     * 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()
+     */
+    public 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)
+        );
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
     /**
      * Move the internal mysql result pointer to the next available result.
      *
@@ -433,7 +474,7 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ freeResult()
 
     /**
      * Places a row from the result set into the given array
@@ -445,10 +486,10 @@ class DB_mysqli extends DB_common
      * 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)
+     * @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
@@ -488,7 +529,7 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ numCols()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -497,7 +538,7 @@ class DB_mysqli extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -505,7 +546,7 @@ class DB_mysqli extends DB_common
      */
     public function freeResult($result)
     {
-        if (! $result instanceof mysqli_result) {
+        if (!$result instanceof mysqli_result) {
             return false;
         }
         mysqli_free_result($result);
@@ -513,7 +554,7 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ numRows()
 
     /**
      * Gets the number of columns in a result set
@@ -522,9 +563,9 @@ class DB_mysqli extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -538,7 +579,7 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ autoCommit()
 
     /**
      * Gets the number of rows in a result set
@@ -547,9 +588,9 @@ class DB_mysqli extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -563,12 +604,12 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ commit()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -582,12 +623,12 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ rollback()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -608,12 +649,12 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ affectedRows()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -634,7 +675,7 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ nextId()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -652,17 +693,14 @@ class DB_mysqli extends DB_common
         }
     }
 
-    // }}}
-    // {{{ 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -675,7 +713,7 @@ class DB_mysqli extends DB_common
             $repeat = 0;
             $this->pushErrorHandling(PEAR_ERROR_RETURN);
             $result = $this->query('UPDATE ' . $seqname
-                                   . ' SET id = LAST_INSERT_ID(id + 1)');
+                . ' SET id = LAST_INSERT_ID(id + 1)');
             $this->popErrorHandling();
             if ($result === DB_OK) {
                 // COMMON CASE
@@ -689,7 +727,7 @@ class DB_mysqli extends DB_common
                 // so fill it and return 1
                 // Obtain a user-level lock
                 $result = $this->getOne('SELECT GET_LOCK('
-                                        . "'${seqname}_lock', 10)");
+                    . "'${seqname}_lock', 10)");
                 if (DB::isError($result)) {
                     return $this->raiseError($result);
                 }
@@ -699,14 +737,14 @@ class DB_mysqli extends DB_common
 
                 // add the default value
                 $result = $this->query('REPLACE INTO ' . $seqname
-                                       . ' (id) VALUES (0)');
+                    . ' (id) VALUES (0)');
                 if (DB::isError($result)) {
                     return $this->raiseError($result);
                 }
 
                 // Release the lock
                 $result = $this->getOne('SELECT RELEASE_LOCK('
-                                        . "'${seqname}_lock')");
+                    . "'${seqname}_lock')");
                 if (DB::isError($result)) {
                     return $this->raiseError($result);
                 }
@@ -726,7 +764,7 @@ class DB_mysqli extends DB_common
                     return 1;
                 }
             } elseif (DB::isError($result) &&
-                      $result->getCode() == DB_ERROR_ALREADY_EXISTS) {
+                $result->getCode() == DB_ERROR_ALREADY_EXISTS) {
                 // BACKWARDS COMPAT
                 // see _BCsequence() comment
                 $result = $this->_BCsequence($seqname);
@@ -740,10 +778,13 @@ class DB_mysqli extends DB_common
         return $this->raiseError($result);
     }
 
+    // }}}
+    // {{{ dropSequence()
+
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -754,8 +795,8 @@ class DB_mysqli extends DB_common
     {
         $seqname = $this->getSequenceName($seq_name);
         $res = $this->query('CREATE TABLE ' . $seqname
-                            . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
-                            . ' PRIMARY KEY(id))');
+            . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+            . ' PRIMARY KEY(id))');
         if (DB::isError($res)) {
             return $res;
         }
@@ -763,24 +804,6 @@ class DB_mysqli extends DB_common
         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()
-     */
-    public function dropSequence($seq_name)
-    {
-        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
-    }
-
     // }}}
     // {{{ _BCsequence()
 
@@ -788,9 +811,9 @@ class DB_mysqli extends DB_common
      * Backwards compatibility with old sequence emulation implementation
      * (clean up the dupes)
      *
-     * @param string $seqname  the sequence name to clean up
+     * @param string $seqname the sequence name to clean up
      *
-     * @return bool  true on success.  A DB_Error object on failure.
+     * @return bool|object
      *
      * @access private
      */
@@ -818,7 +841,7 @@ class DB_mysqli extends DB_common
         // 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");
+            . " WHERE id <> $highest_id");
         if (DB::isError($result)) {
             return $result;
         }
@@ -836,6 +859,24 @@ class DB_mysqli extends DB_common
     // }}}
     // {{{ quoteIdentifier()
 
+    /**
+     * 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()
+     */
+    public function dropSequence($seq_name)
+    {
+        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+    }
+
+    // }}}
+    // {{{ escapeSimple()
+
     /**
      * 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)
@@ -843,7 +884,7 @@ class DB_mysqli extends DB_common
      * 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
+     * @param string $str identifier name to be quoted
      *
      * @return string  quoted identifier string
      *
@@ -856,12 +897,12 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ escapeSimple()
+    // {{{ modifyLimitQuery()
 
     /**
      * Escapes a string according to the current DBMS's standards
      *
-     * @param string $str  the string to be escaped
+     * @param string $str the string to be escaped
      *
      * @return string  the escaped string
      *
@@ -874,15 +915,15 @@ class DB_mysqli extends DB_common
     }
 
     // }}}
-    // {{{ modifyLimitQuery()
+    // {{{ mysqliRaiseError()
 
     /**
      * 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
+     * @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
@@ -901,46 +942,6 @@ class DB_mysqli extends DB_common
         }
     }
 
-    // }}}
-    // {{{ 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()
-     */
-    public 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()
 
@@ -960,14 +961,14 @@ class DB_mysqli extends DB_common
     /**
      * Returns information about a table or a result set
      *
-     * @param object|string  $result  DB_result object from a query or a
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::setOption()
@@ -1019,7 +1020,7 @@ class DB_mysqli extends DB_common
         }
 
         $count = @mysqli_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -1041,16 +1042,16 @@ class DB_mysqli extends DB_common
 
             $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',
+                'name' => $case_func($tmp->name),
+                'type' => isset($this->mysqli_types[$tmp->type])
+                    ? $this->mysqli_types[$tmp->type]
+                    : 'unknown',
                 // http://bugs.php.net/?id=36579
                 //  Doc Bug #36579: mysqli_fetch_field length handling
                 // https://bugs.php.net/bug.php?id=62426
                 //  Bug #62426: mysqli_fetch_field_direct returns incorrect
                 //  length on UTF8 fields
-                'len'   => $tmp->length,
+                'len' => $tmp->length,
                 'flags' => $flags,
             );
 
@@ -1075,7 +1076,7 @@ class DB_mysqli extends DB_common
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -1097,7 +1098,8 @@ class DB_mysqli extends DB_common
         }
     }
 
-    public function getVersion() {
+    public function getVersion()
+    {
         return mysqli_get_server_version($this->connection);
     }
 
index a485ba2fedcd67e1dbc550fc49ec782655d932b3..8fba3cb0f2aef5057fc79444624c7aba9aa3c264 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's oci8 extension
@@ -80,13 +81,13 @@ class DB_oci8 extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'alter',
-        'new_link'      => '5.0.0',
-        'numrows'       => 'subquery',
-        'pconnect'      => true,
-        'prepare'       => true,
-        'ssl'           => false,
-        'transactions'  => true,
+        'limit' => 'alter',
+        'new_link' => '5.0.0',
+        'numrows' => 'subquery',
+        'pconnect' => true,
+        'prepare' => true,
+        'ssl' => false,
+        'transactions' => true,
     );
 
     /**
@@ -94,24 +95,24 @@ class DB_oci8 extends DB_common
      * @var array
      */
     public $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,
+        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,
     );
 
@@ -208,10 +209,10 @@ class DB_oci8 extends DB_common
      *                    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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -237,10 +238,10 @@ class DB_oci8 extends DB_common
                 $connect_function = 'oci_new_connect';
             } else {
                 $connect_function = $persistent ? 'oci_pconnect'
-                                    : 'oci_connect';
+                    : 'oci_connect';
             }
             if (isset($this->dsn['port']) && $this->dsn['port']) {
-                $db = '//'.$db.':'.$this->dsn['port'];
+                $db = '//' . $db . ':' . $this->dsn['port'];
             }
 
             $char = empty($dsn['charset']) ? null : $dsn['charset'];
@@ -355,6 +356,68 @@ class DB_oci8 extends DB_common
     // }}}
     // {{{ nextResult()
 
+    /**
+     * 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
+     */
+    public function modifyQuery($query)
+    {
+        if (preg_match('/^\s*SELECT/i', $query) &&
+            !preg_match('/\sFROM\s/i', $query)) {
+            $query .= ' FROM dual';
+        }
+        return $query;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * 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()
+     */
+    public 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));
+    }
+
+    // }}}
+    // {{{ freeResult()
+
     /**
      * Move the internal oracle result pointer to the next available result
      *
@@ -369,9 +432,6 @@ class DB_oci8 extends DB_common
         return false;
     }
 
-    // }}}
-    // {{{ fetchInto()
-
     /**
      * Places a row from the result set into the given array
      *
@@ -382,10 +442,10 @@ class DB_oci8 extends DB_common
      * 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)
+     * @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
@@ -398,13 +458,13 @@ class DB_oci8 extends DB_common
             return $this->raiseError(DB_ERROR_NOT_CAPABLE);
         }
         if ($fetchmode & DB_FETCHMODE_ASSOC) {
-            $moredata = @OCIFetchInto($result, $arr, OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+            $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);
+            $moredata = OCIFetchInto($result, $arr, OCI_RETURN_NULLS + OCI_RETURN_LOBS);
         }
         if (!$moredata) {
             return null;
@@ -419,7 +479,7 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ numRows()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -428,7 +488,7 @@ class DB_oci8 extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -439,11 +499,14 @@ class DB_oci8 extends DB_common
         return is_resource($result) ? OCIFreeStatement($result) : false;
     }
 
+    // }}}
+    // {{{ numCols()
+
     /**
      * 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?
+     * @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.
      *
@@ -470,7 +533,7 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ prepare()
 
     /**
      * Gets the number of rows in a result set
@@ -482,9 +545,9 @@ class DB_oci8 extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows(), DB_common::setOption()
      */
@@ -493,7 +556,7 @@ class DB_oci8 extends DB_common
         // emulate numRows for Oracle.  yuck.
         if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
             $result === $this->last_stmt) {
-            $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')';
+            $countquery = 'SELECT COUNT(*) FROM (' . $this->last_query . ')';
             $save_query = $this->last_query;
             $save_stmt = $this->last_stmt;
 
@@ -502,7 +565,7 @@ class DB_oci8 extends DB_common
             // 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);
@@ -514,7 +577,7 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ execute()
 
     /**
      * Gets the number of columns in a result set
@@ -523,9 +586,9 @@ class DB_oci8 extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -539,7 +602,145 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ prepare()
+    // {{{ 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.
+     */
+    public function autoCommit($onoff = false)
+    {
+        $this->autocommit = (bool)$onoff;;
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ commit()
+
+    /**
+     * Commits the current transaction
+     *
+     * @return int|object
+     */
+    public function commit()
+    {
+        $result = @OCICommit($this->connection);
+        if (!$result) {
+            return $this->oci8RaiseError();
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * Reverts the current transaction
+     *
+     * @return int|object
+     */
+    public 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|object
+     */
+    public 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()
+
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ modifyLimitQuery()
 
     /**
      * Prepares a query for multiple execution with execute().
@@ -562,7 +763,7 @@ class DB_oci8 extends DB_common
      *    "UPDATE foo SET col=? WHERE col='over \& under'"
      * </code>
      *
-     * @param string $query  the query to be prepared
+     * @param string $query the query to be prepared
      *
      * @return mixed  DB statement resource on success. DB_Error on failure.
      *
@@ -570,15 +771,15 @@ class DB_oci8 extends DB_common
      */
     public function prepare($query)
     {
-        $tokens   = preg_split(
+        $tokens = preg_split(
             '/((?<!\\\)[&?!])/',
             $query,
             -1,
             PREG_SPLIT_DELIM_CAPTURE
         );
-        $binds    = count($tokens) - 1;
-        $token    = 0;
-        $types    = array();
+        $binds = count($tokens) - 1;
+        $token = 0;
+        $types = array();
         $newquery = '';
 
         foreach ($tokens as $key => $val) {
@@ -617,7 +818,7 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ execute()
+    // {{{ nextId()
 
     /**
      * Executes a DB statement prepared with prepare().
@@ -626,8 +827,8 @@ class DB_oci8 extends DB_common
      * 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
+     * @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
@@ -713,179 +914,31 @@ class DB_oci8 extends DB_common
         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.
-     */
-    public 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.
-     */
-    public 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.
-     */
-    public 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.
-     */
-    public 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
-     */
-    public 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
+     * Formats a float value for use within a query in a locale-independent
+     * manner.
      *
-     * @access protected
+     * @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.
      */
-    public function modifyLimitQuery($query, $from, $count, $params = array())
+    public function quoteFloat($float)
     {
-        // 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;
+        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ dropSequence()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -917,10 +970,13 @@ class DB_oci8 extends DB_common
         return $arr[0];
     }
 
+    // }}}
+    // {{{ oci8RaiseError()
+
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -930,16 +986,16 @@ class DB_oci8 extends DB_common
     public function createSequence($seq_name)
     {
         return $this->query('CREATE SEQUENCE '
-                            . $this->getSequenceName($seq_name));
+            . $this->getSequenceName($seq_name));
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ errorNative()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -949,50 +1005,11 @@ class DB_oci8 extends DB_common
     public function dropSequence($seq_name)
     {
         return $this->query('DROP SEQUENCE '
-                            . $this->getSequenceName($seq_name));
+            . $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()
-     */
-    public 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()
+    // {{{ tableInfo()
 
     /**
      * Gets the DBMS' native error code produced by the last query
@@ -1014,7 +1031,7 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ tableInfo()
+    // {{{ getSpecialQuery()
 
     /**
      * Returns information about a table or a result set
@@ -1024,14 +1041,14 @@ class DB_oci8 extends DB_common
      *
      * NOTE: flags won't contain index information.
      *
-     * @param object|string  $result  DB_result object from a query or a
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -1053,9 +1070,9 @@ class DB_oci8 extends DB_common
              */
             $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";
+                . 'nullable '
+                . 'FROM user_tab_columns '
+                . "WHERE table_name='$result' ORDER BY column_id";
 
             $this->last_query = $q_fields;
 
@@ -1065,14 +1082,14 @@ class DB_oci8 extends DB_common
             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),
+                    '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) {
@@ -1107,9 +1124,9 @@ class DB_oci8 extends DB_common
                 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),
+                        'name' => $case_func(@OCIColumnName($result, $i + 1)),
+                        'type' => @OCIColumnType($result, $i + 1),
+                        'len' => @OCIColumnSize($result, $i + 1),
                         'flags' => '',
                     );
                     if ($mode & DB_TABLEINFO_ORDER) {
@@ -1127,12 +1144,12 @@ class DB_oci8 extends DB_common
     }
 
     // }}}
-    // {{{ getSpecialQuery()
+    // {{{ quoteFloat()
 
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -1154,23 +1171,6 @@ class DB_oci8 extends DB_common
         }
     }
 
-    // }}}
-    // {{{ 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.
-     */
-    public function quoteFloat($float)
-    {
-        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
-    }
-     
     // }}}
 }
 
index abd689ef0efa0b29204a79f05985154c666f157d..0d1e11a47975ee1806f7764ab6f2e18dd8c3b836 100644 (file)
@@ -27,7 +27,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's odbc extension
@@ -82,13 +83,13 @@ class DB_odbc extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'emulate',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => false,
+        'limit' => 'emulate',
+        'new_link' => false,
+        'numrows' => true,
+        'pconnect' => true,
+        'prepare' => false,
+        'ssl' => false,
+        'transactions' => false,
     );
 
     /**
@@ -173,10 +174,10 @@ class DB_odbc extends DB_common
      * 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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -242,10 +243,26 @@ class DB_odbc extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * Gets the DBMS' native error code and message produced by the last query
+     *
+     * @return string  the DBMS' error code and message
+     */
+    public function errorNative()
+    {
+        if (!is_resource($this->connection)) {
+            return @odbc_error() . ' ' . @odbc_errormsg();
+        }
+        return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
     /**
      * Disconnects from the database server
      *
-     * @return bool  TRUE on success, FALSE on failure
+     * @return bool|void
      */
     public function disconnect()
     {
@@ -255,7 +272,7 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ nextResult()
 
     /**
      * Sends a query to the database server
@@ -285,7 +302,75 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ nextResult()
+    // {{{ fetchInto()
+
+    /**
+     * 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()
+     */
+    public 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()
+        );
+    }
+
+    // }}}
+    // {{{ freeResult()
 
     /**
      * Move the internal odbc result pointer to the next available result
@@ -302,7 +387,7 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ numCols()
 
     /**
      * Places a row from the result set into the given array
@@ -314,10 +399,10 @@ class DB_odbc extends DB_common
      * 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)
+     * @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
@@ -342,7 +427,7 @@ class DB_odbc extends DB_common
         }
         if ($fetchmode !== DB_FETCHMODE_ORDERED) {
             for ($i = 0; $i < count($arr); $i++) {
-                $colName = @odbc_field_name($result, $i+1);
+                $colName = @odbc_field_name($result, $i + 1);
                 $a[$colName] = $arr[$i];
             }
             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
@@ -360,7 +445,7 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ affectedRows()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -369,7 +454,7 @@ class DB_odbc extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -381,7 +466,7 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ numRows()
 
     /**
      * Gets the number of columns in a result set
@@ -390,9 +475,9 @@ class DB_odbc extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -406,14 +491,14 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ quoteIdentifier()
 
     /**
      * 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.
+     * @return int|object
      */
     public function affectedRows()
     {
@@ -428,7 +513,7 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ numRows()
+    // {{{ nextId()
 
     /**
      * Gets the number of rows in a result set
@@ -440,9 +525,9 @@ class DB_odbc extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -458,16 +543,13 @@ class DB_odbc extends DB_common
         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
+     * @param string $str identifier name to be quoted
      *
      * @return string  quoted identifier string
      *
@@ -491,16 +573,16 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ dropSequence()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -546,10 +628,13 @@ class DB_odbc extends DB_common
         return $row[0];
     }
 
+    // }}}
+    // {{{ autoCommit()
+
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -559,18 +644,18 @@ class DB_odbc extends DB_common
     public function createSequence($seq_name)
     {
         return $this->query('CREATE TABLE '
-                            . $this->getSequenceName($seq_name)
-                            . ' (id integer NOT NULL,'
-                            . ' PRIMARY KEY(id))');
+            . $this->getSequenceName($seq_name)
+            . ' (id integer NOT NULL,'
+            . ' PRIMARY KEY(id))');
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ commit()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -583,14 +668,14 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ autoCommit()
+    // {{{ rollback()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @param bool $onoff true turns it on, false turns it off
      *
-     * @return int  DB_OK on success.  A DB_Error object if the driver
+     * @return int|object
      *               doesn't support auto-committing transactions.
      */
     public function autoCommit($onoff = false)
@@ -602,12 +687,12 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ odbcRaiseError()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -618,12 +703,12 @@ class DB_odbc extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ errorNative()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -633,104 +718,20 @@ class DB_odbc extends DB_common
         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()
-     */
-    public 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
-     */
-    public 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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -776,7 +777,7 @@ class DB_odbc extends DB_common
         }
 
         $count = @odbc_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -786,9 +787,9 @@ class DB_odbc extends DB_common
             $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),
+                '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) {
@@ -814,9 +815,9 @@ class DB_odbc extends DB_common
      *
      * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @param string $type the kind of objects you want to retrieve
      *
-     * @return string  the list of objects requested
+     * @return array|string
      *
      * @access protected
      * @see DB_common::getListOf()
@@ -859,7 +860,7 @@ class DB_odbc extends DB_common
          * in the odbc_tables() call because some backends choke on this:
          *     odbc_tables($this->connection, '', '', '', 'TABLE')
          */
-        $res  = @odbc_tables($this->connection);
+        $res = @odbc_tables($this->connection);
         if (!$res) {
             return $this->odbcRaiseError();
         }
index a10774cc69d7a5245bc35bc45f537efa20ae3ee7..251f9d7a89378307778e5b2921b9e691ff201931 100644 (file)
@@ -28,7 +28,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's pgsql extension
@@ -76,21 +77,20 @@ class DB_pgsql extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'alter',
-        'new_link'      => '4.3.0',
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => true,
-        'transactions'  => true,
+        '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
      */
-    public $errorcode_map = array(
-    );
+    public $errorcode_map = array();
 
     /**
      * The raw database connection created by PHP
@@ -194,15 +194,15 @@ class DB_pgsql extends DB_common
      * );
      *
      * $db = DB::connect($dsn, $options);
-     * if (PEAR::isError($db)) {
+     * if ((new PEAR)->isError($db)) {
      *     die($db->getMessage());
      * }
      * </code>
      *
-     * @param array $dsn         the data source name
-     * @param bool  $persistent  should the connection be persistent?
+     * @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.
+     * @return int|object
      *
      * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
      */
@@ -381,6 +381,161 @@ class DB_pgsql extends DB_common
     // }}}
     // {{{ nextResult()
 
+    /**
+     * 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
+     */
+    public function _checkManip($query)
+    {
+        return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query)
+            || parent::_checkManip($query));
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * 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()
+     */
+    public 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);
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * 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
+     */
+    public function errorNative()
+    {
+        return @pg_errormessage($this->connection);
+    }
+
+    // }}}
+    // {{{ quoteBoolean()
+
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ escapeSimple()
+
+    /**
+     * 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|object
+     *
+     * @see DB_result::numRows()
+     */
+    public function numRows($result)
+    {
+        $rows = @pg_numrows($result);
+        if ($rows === null) {
+            return $this->pgsqlRaiseError();
+        }
+        return $rows;
+    }
+
+    // }}}
+    // {{{ numCols()
+
     /**
      * Move the internal pgsql result pointer to the next available result
      *
@@ -396,7 +551,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ numRows()
 
     /**
      * Places a row from the result set into the given array
@@ -408,10 +563,10 @@ class DB_pgsql extends DB_common
      * 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)
+     * @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
@@ -447,7 +602,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ autoCommit()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -456,7 +611,7 @@ class DB_pgsql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -474,7 +629,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ quoteBoolean()
+    // {{{ commit()
 
     /**
      * Formats a boolean value for use within a query in a locale-independent
@@ -489,9 +644,9 @@ class DB_pgsql extends DB_common
     {
         return $boolean ? 'TRUE' : 'FALSE';
     }
-     
+
     // }}}
-    // {{{ escapeSimple()
+    // {{{ rollback()
 
     /**
      * Escapes a string according to the current DBMS's standards
@@ -499,7 +654,7 @@ class DB_pgsql extends DB_common
      * {@internal PostgreSQL treats a backslash as an escape character,
      * so they are escaped as well.
      *
-     * @param string $str  the string to be escaped
+     * @param string $str the string to be escaped
      *
      * @return string  the escaped string
      *
@@ -526,7 +681,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ affectedRows()
 
     /**
      * Gets the number of columns in a result set
@@ -535,9 +690,9 @@ class DB_pgsql extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -551,37 +706,12 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ 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()
-     */
-    public function numRows($result)
-    {
-        $rows = @pg_numrows($result);
-        if ($rows === null) {
-            return $this->pgsqlRaiseError();
-        }
-        return $rows;
-    }
-
-    // }}}
-    // {{{ autoCommit()
+    // {{{ nextId()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -595,12 +725,12 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ createSequence()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -617,12 +747,12 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ dropSequence()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -637,7 +767,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ modifyLimitQuery()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -652,16 +782,16 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ pgsqlRaiseError()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -697,12 +827,12 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ createSequence()
+    // {{{ errorNative()
 
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -717,12 +847,12 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ errorCode()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -732,19 +862,19 @@ class DB_pgsql extends DB_common
     public function dropSequence($seq_name)
     {
         return $this->query('DROP SEQUENCE '
-                            . $this->getSequenceName($seq_name));
+            . $this->getSequenceName($seq_name));
     }
 
     // }}}
-    // {{{ modifyLimitQuery()
+    // {{{ tableInfo()
 
     /**
      * 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
+     * @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
@@ -760,116 +890,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ 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()
-     */
-    public 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
-     */
-    public 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
-     */
-    public 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()
+    // {{{ _pgFieldFlags()
 
     /**
      * Returns information about a table or a result set
@@ -877,14 +898,14 @@ class DB_pgsql extends DB_common
      * 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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -926,7 +947,7 @@ class DB_pgsql extends DB_common
         }
 
         $count = @pg_numfields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -935,12 +956,12 @@ class DB_pgsql extends DB_common
         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),
+                '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)
-                           : '',
+                    ? $this->_pgFieldFlags($id, $i, $result)
+                    : '',
             );
             if ($mode & DB_TABLEINFO_ORDER) {
                 $res['order'][$res[$i]['name']] = $i;
@@ -958,7 +979,7 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ _pgFieldFlags()
+    // {{{ getSpecialQuery()
 
     /**
      * Get a column's flags
@@ -967,9 +988,10 @@ class DB_pgsql extends DB_common
      * 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
+     * @param int $resource the PostgreSQL result identifier
+     * @param int $num_field the field number
      *
+     * @param $table_name
      * @return string  the flags
      *
      * @access private
@@ -997,7 +1019,7 @@ class DB_pgsql extends DB_common
                                 AND $tableWhere");
         if (@pg_numrows($result) > 0) {
             $row = @pg_fetch_row($result, 0);
-            $flags  = ($row[0] == 't') ? 'not_null ' : '';
+            $flags = ($row[0] == 't') ? 'not_null ' : '';
 
             if ($row[1] == 't') {
                 $result = @pg_exec($this->connection, "SELECT a.adsrc
@@ -1021,7 +1043,7 @@ class DB_pgsql extends DB_common
                                 AND $tableWhere");
         $count = @pg_numrows($result);
 
-        for ($i = 0; $i < $count ; $i++) {
+        for ($i = 0; $i < $count; $i++) {
             $row = @pg_fetch_row($result, $i);
             $keys = explode(' ', $row[2]);
 
@@ -1038,12 +1060,12 @@ class DB_pgsql extends DB_common
     }
 
     // }}}
-    // {{{ getSpecialQuery()
+    // {{{ _checkManip()
 
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -1056,37 +1078,37 @@ class DB_pgsql extends DB_common
         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_'";
+                    . ' 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')";
+                    . ' 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')";
+                    . " 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')";
+                    . " NOT IN ('information_schema', 'pg_catalog')";
             case 'users':
                 // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd  |valuntil
                 return 'SELECT usename FROM pg_user';
@@ -1099,27 +1121,6 @@ class DB_pgsql extends DB_common
                 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
-     */
-    public function _checkManip($query)
-    {
-        return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query)
-                || parent::_checkManip($query));
-    }
 }
 
 /*
index 33d36060a40423efcf688cb9f978cabd3a405c06..5e7c67b21ae5486d84c278631ae88ec85ac32557 100644 (file)
@@ -28,7 +28,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's sqlite extension
@@ -80,13 +81,13 @@ class DB_sqlite extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'alter',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => false,
+        'limit' => 'alter',
+        'new_link' => false,
+        'numrows' => true,
+        'pconnect' => true,
+        'prepare' => false,
+        'ssl' => false,
+        'transactions' => false,
     );
 
     /**
@@ -98,8 +99,7 @@ class DB_sqlite extends DB_common
      *
      * @var array
      */
-    public $errorcode_map = array(
-    );
+    public $errorcode_map = array();
 
     /**
      * The raw database connection created by PHP
@@ -122,22 +122,22 @@ class DB_sqlite extends DB_common
      * @var array
      */
     public $keywords = array(
-        'BLOB'      => '',
-        'BOOLEAN'   => '',
+        'BLOB' => '',
+        'BOOLEAN' => '',
         'CHARACTER' => '',
-        'CLOB'      => '',
-        'FLOAT'     => '',
-        'INTEGER'   => '',
-        'KEY'       => '',
-        'NATIONAL'  => '',
-        'NUMERIC'   => '',
-        'NVARCHAR'  => '',
-        'PRIMARY'   => '',
-        'TEXT'      => '',
+        'CLOB' => '',
+        'FLOAT' => '',
+        'INTEGER' => '',
+        'KEY' => '',
+        'NATIONAL' => '',
+        'NUMERIC' => '',
+        'NVARCHAR' => '',
+        'PRIMARY' => '',
+        'TEXT' => '',
         'TIMESTAMP' => '',
-        'UNIQUE'    => '',
-        'VARCHAR'   => '',
-        'VARYING'   => '',
+        'UNIQUE' => '',
+        'VARCHAR' => '',
+        'VARYING' => '',
     );
 
     /**
@@ -183,15 +183,15 @@ class DB_sqlite extends DB_common
      * );
      *
      * $db = DB::connect($dsn, $options);
-     * if (PEAR::isError($db)) {
+     * if ((new PEAR)->isError($db)) {
      *     die($db->getMessage());
      * }
      * </code>
      *
-     * @param array $dsn         the data source name
-     * @param bool  $persistent  should the connection be persistent?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -255,10 +255,98 @@ class DB_sqlite extends DB_common
     // }}}
     // {{{ disconnect()
 
+    /**
+     * 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()
+     */
+    public 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);
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * 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
+     */
+    public function errorNative()
+    {
+        return $this->_lasterror;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
     /**
      * Disconnects from the database server
      *
-     * @return bool  TRUE on success, FALSE on failure
+     * @return bool|void
      */
     public function disconnect()
     {
@@ -268,7 +356,7 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ simpleQuery()
+    // {{{ freeResult()
 
     /**
      * Sends a query to the database server
@@ -313,12 +401,68 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ nextResult()
+    // {{{ numCols()
+
+    /**
+     * 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()
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ 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|object
+     *
+     * @see DB_result::numRows()
+     */
+    public function numRows($result)
+    {
+        $rows = @sqlite_num_rows($result);
+        if ($rows === null) {
+            return $this->sqliteRaiseError();
+        }
+        return $rows;
+    }
+
+    // }}}
+    // {{{ affected()
 
     /**
      * Move the internal sqlite result pointer to the next available result
      *
-     * @param resource $result  the valid sqlite result resource
+     * @param resource $result the valid sqlite result resource
      *
      * @return bool  true if a result is available otherwise return false
      */
@@ -328,7 +472,7 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ dropSequence()
 
     /**
      * Places a row from the result set into the given array
@@ -340,10 +484,10 @@ class DB_sqlite extends DB_common
      * 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)
+     * @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
@@ -392,9 +536,6 @@ class DB_sqlite extends DB_common
         return DB_OK;
     }
 
-    // }}}
-    // {{{ freeResult()
-
     /**
      * Deletes the result set and frees the memory occupied by the result set
      *
@@ -402,7 +543,7 @@ class DB_sqlite extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -419,7 +560,7 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ nextId()
 
     /**
      * Gets the number of columns in a result set
@@ -428,9 +569,9 @@ class DB_sqlite extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -444,32 +585,7 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ 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()
-     */
-    public function numRows($result)
-    {
-        $rows = @sqlite_num_rows($result);
-        if ($rows === null) {
-            return $this->sqliteRaiseError();
-        }
-        return $rows;
-    }
-
-    // }}}
-    // {{{ affected()
+    // {{{ getDbFileStats()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -484,12 +600,12 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ escapeSimple()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -501,46 +617,17 @@ class DB_sqlite extends DB_common
         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()
-     */
-    public 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()
+    // {{{ modifyLimitQuery()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -561,7 +648,7 @@ class DB_sqlite extends DB_common
                     return $id;
                 }
             } elseif ($ondemand && DB::isError($result) &&
-                      $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
                 $result = $this->createSequence($seq_name);
                 if (DB::isError($result)) {
                     return $this->raiseError($result);
@@ -575,7 +662,39 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ getDbFileStats()
+    // {{{ modifyQuery()
+
+    /**
+     * 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()
+     */
+    public 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);
+        //}
+    }
+
+    // }}}
+    // {{{ sqliteRaiseError()
 
     /**
      * Get the file stats for the current database
@@ -584,7 +703,7 @@ class DB_sqlite extends DB_common
      * atime, mtime, ctime, blksize, blocks or a numeric key between
      * 0 and 12.
      *
-     * @param string $arg  the array key for stats()
+     * @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
@@ -600,17 +719,17 @@ class DB_sqlite extends DB_common
                 if (((int)$arg <= 12) & ((int)$arg >= 0)) {
                     return false;
                 }
-                return $stats[$arg ];
+                return $stats[$arg];
             }
             if (array_key_exists(trim($arg), $stats)) {
-                return $stats[$arg ];
+                return $stats[$arg];
             }
         }
         return $stats;
     }
 
     // }}}
-    // {{{ escapeSimple()
+    // {{{ errorNative()
 
     /**
      * Escapes a string according to the current DBMS's standards
@@ -620,7 +739,7 @@ class DB_sqlite extends DB_common
      * 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
+     * @param string $str the string to be escaped
      *
      * @return string  the escaped string
      *
@@ -633,15 +752,15 @@ class DB_sqlite extends DB_common
     }
 
     // }}}
-    // {{{ modifyLimitQuery()
+    // {{{ errorCode()
 
     /**
      * 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
+     * @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
@@ -656,135 +775,16 @@ class DB_sqlite extends DB_common
         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()
-     */
-    public 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()
-     */
-    public 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
-     */
-    public 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
-     */
-    public 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
+     * @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.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -811,7 +811,7 @@ class DB_sqlite extends DB_common
                 null,
                 null,
                 'This DBMS can not obtain tableInfo' .
-                                     ' from result sets'
+                ' from result sets'
             );
         }
 
@@ -822,7 +822,7 @@ class DB_sqlite extends DB_common
         }
 
         $count = count($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -832,10 +832,10 @@ class DB_sqlite extends DB_common
             if (strpos($id[$i]['type'], '(') !== false) {
                 $bits = explode('(', $id[$i]['type']);
                 $type = $bits[0];
-                $len  = rtrim($bits[1], ')');
+                $len = rtrim($bits[1], ')');
             } else {
                 $type = $id[$i]['type'];
-                $len  = 0;
+                $len = 0;
             }
 
             $flags = '';
@@ -855,9 +855,9 @@ class DB_sqlite extends DB_common
 
             $res[$i] = array(
                 'table' => $case_func($result),
-                'name'  => $case_func($id[$i]['name']),
-                'type'  => $type,
-                'len'   => $len,
+                'name' => $case_func($id[$i]['name']),
+                'type' => $type,
+                'len' => $len,
                 'flags' => $flags,
             );
 
@@ -878,8 +878,8 @@ class DB_sqlite extends DB_common
     /**
      * 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
+     * @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.
      *
@@ -906,13 +906,13 @@ class DB_sqlite extends DB_common
                 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;";
+                    . '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;';
+                    . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+                    . "WHERE type!='meta' "
+                    . 'ORDER BY tbl_name, type DESC, name;';
             case 'schemax':
             case 'schema_x':
                 /*
@@ -921,10 +921,10 @@ class DB_sqlite extends DB_common
                  *                   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;';
+                    . '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
index 8a06403eca0952755fb1b5d8147d47928dc075b4..2ddc4c0b493e74be10a066e8e2ace8218ee7f73b 100644 (file)
@@ -46,33 +46,33 @@ 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 */
+     * updates in views) we hold data from */
     public $_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 */
+     * string for single-column primary keys, or an array of strings
+     * for multiple-column primary keys */
     public $_keycolumn = null;
 
     /** DB connection handle used for all transactions */
     public $_dbh = null;
 
     /** an assoc with the names of database fields stored as properties
-        in this object */
+     * in this object */
     public $_properties = array();
 
     /** an assoc with the names of the properties in this object that
-        have been changed since they were fetched from the database */
+     * have been changed since they were fetched from the database */
     public $_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. */
+     * objects that don't have their table's key column in their
+     * property lists will be flagged as read-only. */
     public $_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 */
+     * are set, this validator function returns true if the field is
+     * valid, false if not */
     public $_validator = null;
 
     // }}}
@@ -108,49 +108,34 @@ class DB_storage extends PEAR
     // {{{ _makeWhere()
 
     /**
-     * Utility method to build a "WHERE" clause to locate ourselves in
-     * the table.
-     *
-     * XXX future improvement: use rowids?
-     *
-     * @access private
+     * Create a new (empty) row in the configured table for this
+     * object.
+     * @param $newpk
+     * @return |null
      */
-    public function _makeWhere($keyval = null)
+    public function insert($newpk)
     {
         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]);
-                }
-            }
+            $primarykey = $this->_keycolumn;
         } 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);
-            }
+            $primarykey = array($this->_keycolumn);
         }
-        return $whereclause;
+        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);
+        return null;
     }
 
     // }}}
@@ -162,7 +147,7 @@ class DB_storage extends PEAR
      *
      * @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
+     * @return int|object
      */
     public function setup($keyval)
     {
@@ -198,31 +183,51 @@ class DB_storage extends PEAR
     // {{{ insert()
 
     /**
-     * Create a new (empty) row in the configured table for this
-     * object.
+     * Utility method to build a "WHERE" clause to locate ourselves in
+     * the table.
+     *
+     * XXX future improvement: use rowids?
+     *
+     * @access private
+     * @param null $keyval
+     * @return mixed|string|null
      */
-    public function insert($newpk)
+    public function _makeWhere($keyval = null)
     {
         if (is_array($this->_keycolumn)) {
-            $primarykey = $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 {
-            $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];
+            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);
+            }
         }
-        $this->setup($newpk);
+        return $whereclause;
     }
 
     // }}}
@@ -293,6 +298,7 @@ class DB_storage extends PEAR
 
     /**
      * Static method used to create new DB storage objects.
+     * @param $table
      * @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
@@ -365,6 +371,9 @@ class DB_storage extends PEAR
 
     /**
      * Modify an attriute value.
+     * @param $property
+     * @param $newvalue
+     * @return bool|object
      */
     public function set($property, $newvalue)
     {
@@ -469,7 +478,7 @@ class DB_storage extends PEAR
     /**
      * Stores changes to this object in the database.
      *
-     * @return DB_OK or a DB error
+     * @return DB_OK|int
      */
     public function store()
     {
@@ -514,7 +523,7 @@ class DB_storage extends PEAR
                 true
             );
         }
-        $query = 'DELETE FROM ' . $this->_table .' WHERE '.
+        $query = 'DELETE FROM ' . $this->_table . ' WHERE ' .
             $this->_makeWhere();
         $res = $this->_dbh->query($query);
         if (DB::isError($res)) {
index c2108059dd4cf3c247a19056ec49f0d86e20676f..01f2121d48c91e7c03c53ab961261758ba7c6b09 100644 (file)
@@ -17,7 +17,7 @@
  * @category   Database
  * @package    DB
  * @author     Sterling Hughes <sterling@php.net>
- * @author     Antônio Carlos Venâncio Júnior <floripa@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
@@ -28,7 +28,8 @@
 /**
  * Obtain the DB_common class so it can be extended from
  */
-require_once 'DB/common.php';
+//require_once 'DB/common.php';
+require_once 'common.php';
 
 /**
  * The methods PEAR DB uses to interact with PHP's sybase extension
@@ -42,7 +43,7 @@ require_once 'DB/common.php';
  * @category   Database
  * @package    DB
  * @author     Sterling Hughes <sterling@php.net>
- * @author     Antônio Carlos Venâncio Júnior <floripa@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
@@ -79,21 +80,20 @@ class DB_sybase extends DB_common
      * @var array
      */
     public $features = array(
-        'limit'         => 'emulate',
-        'new_link'      => false,
-        'numrows'       => true,
-        'pconnect'      => true,
-        'prepare'       => false,
-        'ssl'           => false,
-        'transactions'  => true,
+        '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
      */
-    public $errorcode_map = array(
-    );
+    public $errorcode_map = array();
 
     /**
      * The raw database connection created by PHP
@@ -164,10 +164,10 @@ class DB_sybase extends DB_common
      *   + 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?
+     * @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.
+     * @return int|object
      */
     public function connect($dsn, $persistent = false)
     {
@@ -291,6 +291,105 @@ class DB_sybase extends DB_common
     // }}}
     // {{{ nextResult()
 
+    /**
+     * 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()
+     */
+    public function sybaseRaiseError($errno = null)
+    {
+        $native = $this->errorNative();
+        if ($errno === null) {
+            $errno = $this->errorCode($native);
+        }
+        return $this->raiseError($errno, null, null, null, $native);
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Gets the DBMS' native error message produced by the last query
+     *
+     * @return string  the DBMS' error message
+     */
+    public function errorNative()
+    {
+        return @sybase_get_last_message();
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * 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
+     */
+    public 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;
+    }
+
+    // }}}
+    // {{{ numCols()
+
     /**
      * Move the internal sybase result pointer to the next available result
      *
@@ -306,7 +405,7 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ fetchInto()
+    // {{{ numRows()
 
     /**
      * Places a row from the result set into the given array
@@ -318,10 +417,10 @@ class DB_sybase extends DB_common
      * 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)
+     * @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
@@ -366,7 +465,7 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ freeResult()
+    // {{{ affectedRows()
 
     /**
      * Deletes the result set and frees the memory occupied by the result set
@@ -375,7 +474,7 @@ class DB_sybase extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
      * @return bool  TRUE on success, FALSE if $result is invalid
      *
@@ -387,7 +486,7 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ numCols()
+    // {{{ nextId()
 
     /**
      * Gets the number of columns in a result set
@@ -396,9 +495,9 @@ class DB_sybase extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of columns.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numCols()
      */
@@ -411,9 +510,6 @@ class DB_sybase extends DB_common
         return $cols;
     }
 
-    // }}}
-    // {{{ numRows()
-
     /**
      * Gets the number of rows in a result set
      *
@@ -421,9 +517,9 @@ class DB_sybase extends DB_common
      * 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
+     * @param resource $result PHP's query result resource
      *
-     * @return int  the number of rows.  A DB_Error object on failure.
+     * @return int|object
      *
      * @see DB_result::numRows()
      */
@@ -437,7 +533,7 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ affectedRows()
+    // {{{ dropSequence()
 
     /**
      * Determines the number of rows affected by a data maniuplation query
@@ -457,16 +553,16 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ nextId()
+    // {{{ quoteFloat()
 
     /**
      * 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
+     * @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.
+     * @return int|object
      *               A DB_Error object on failure.
      *
      * @see DB_common::nextID(), DB_common::getSequenceName(),
@@ -504,10 +600,13 @@ class DB_sybase extends DB_common
         return $result[0];
     }
 
+    // }}}
+    // {{{ autoCommit()
+
     /**
      * Creates a new sequence
      *
-     * @param string $seq_name  name of the new sequence
+     * @param string $seq_name name of the new sequence
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -517,18 +616,18 @@ class DB_sybase extends DB_common
     public function createSequence($seq_name)
     {
         return $this->query('CREATE TABLE '
-                            . $this->getSequenceName($seq_name)
-                            . ' (id numeric(10, 0) IDENTITY NOT NULL,'
-                            . ' vapor int NULL)');
+            . $this->getSequenceName($seq_name)
+            . ' (id numeric(10, 0) IDENTITY NOT NULL,'
+            . ' vapor int NULL)');
     }
 
     // }}}
-    // {{{ dropSequence()
+    // {{{ commit()
 
     /**
      * Deletes a sequence
      *
-     * @param string $seq_name  name of the sequence to be deleted
+     * @param string $seq_name name of the sequence to be deleted
      *
      * @return int  DB_OK on success.  A DB_Error object on failure.
      *
@@ -541,7 +640,7 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ quoteFloat()
+    // {{{ rollback()
 
     /**
      * Formats a float value for use within a query in a locale-independent
@@ -556,14 +655,14 @@ class DB_sybase extends DB_common
     {
         return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
     }
-     
+
     // }}}
-    // {{{ autoCommit()
+    // {{{ sybaseRaiseError()
 
     /**
      * Enables or disables automatic commits
      *
-     * @param bool $onoff  true turns it on, false turns it off
+     * @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.
@@ -577,12 +676,12 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ commit()
+    // {{{ errorNative()
 
     /**
      * Commits the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function commit()
     {
@@ -600,12 +699,12 @@ class DB_sybase extends DB_common
     }
 
     // }}}
-    // {{{ rollback()
+    // {{{ errorCode()
 
     /**
      * Reverts the current transaction
      *
-     * @return int  DB_OK on success.  A DB_Error object on failure.
+     * @return int|object
      */
     public function rollback()
     {
@@ -622,105 +721,6 @@ class DB_sybase extends DB_common
         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()
-     */
-    public 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
-     */
-    public 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
-     */
-    public 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()
 
@@ -730,14 +730,14 @@ class DB_sybase extends DB_common
      * 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
+     * @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
+     * @param int $mode a valid tableInfo mode
      *
-     * @return array  an associative array with the information requested.
+     * @return array|object
      *                 A DB_Error object on failure.
      *
      * @see DB_common::tableInfo()
@@ -786,7 +786,7 @@ class DB_sybase extends DB_common
         }
 
         $count = @sybase_num_fields($id);
-        $res   = array();
+        $res = array();
 
         if ($mode) {
             $res['num_fields'] = $count;
@@ -797,11 +797,11 @@ class DB_sybase extends DB_common
             // 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,
+                    ? $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']) {
@@ -835,8 +835,8 @@ class DB_sybase extends DB_common
      *  + <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
+     * @param string $table the table name
+     * @param string $column the field name
      *
      * @return string  space delimited string of flags.  Empty string if none.
      *
@@ -888,7 +888,7 @@ class DB_sybase extends DB_common
         }
 
         if (array_key_exists($column, $flags)) {
-            return(implode(' ', $flags[$column]));
+            return (implode(' ', $flags[$column]));
         }
 
         return '';
@@ -901,8 +901,8 @@ class DB_sybase extends DB_common
      * 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
+     * @param array $array reference of flags array to add a value to
+     * @param mixed $value value to add to the flag array
      *
      * @return void
      *
@@ -923,7 +923,7 @@ class DB_sybase extends DB_common
     /**
      * Obtains the query string needed for listing a given type of objects
      *
-     * @param string $type  the kind of objects you want to retrieve
+     * @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
@@ -936,7 +936,7 @@ class DB_sybase extends DB_common
         switch ($type) {
             case 'tables':
                 return "SELECT name FROM sysobjects WHERE type = 'U'"
-                       . ' ORDER BY name';
+                    . ' ORDER BY name';
             case 'views':
                 return "SELECT name FROM sysobjects WHERE type = 'V'";
             default: