Loudblog PHP Cross Reference Blogging Systems

Source: /loudblog/inc/database/adodb.inc.php - 4214 lines - 120323 bytes - Summary - Text - Print

   1  <?php 
   2  /*
   3   * Set tabs to 4 for best viewing.
   4   * 
   5   * Latest version is available at http://adodb.sourceforge.net
   6   * 
   7   * This is the main include file for ADOdb.
   8   * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
   9   *
  10   * The ADOdb files are formatted so that doxygen can be used to generate documentation.
  11   * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
  12   */
  13  
  14  /**
  15      \mainpage     
  16      
  17       @version V4.92a 29 Aug 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
  18  
  19      Released under both BSD license and Lesser GPL library license. You can choose which license
  20      you prefer.
  21      
  22      PHP's database access functions are not standardised. This creates a need for a database 
  23      class library to hide the differences between the different database API's (encapsulate 
  24      the differences) so we can easily switch databases.
  25  
  26      We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
  27      Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
  28      ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
  29      other databases via ODBC.
  30  
  31      Latest Download at http://adodb.sourceforge.net/
  32        
  33   */
  34   
  35   if (!defined('_ADODB_LAYER')) {
  36       define('_ADODB_LAYER',1);
  37      
  38      //==============================================================================================    
  39      // CONSTANT DEFINITIONS
  40      //==============================================================================================    
  41  
  42  
  43      /** 
  44       * Set ADODB_DIR to the directory where this file resides...
  45       * This constant was formerly called $ADODB_RootPath
  46       */
  47      if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
  48      
  49      //==============================================================================================    
  50      // GLOBAL VARIABLES
  51      //==============================================================================================    
  52  
  53      GLOBAL 
  54          $ADODB_vers,         // database version
  55          $ADODB_COUNTRECS,    // count number of records returned - slows down query
  56          $ADODB_CACHE_DIR,    // directory to cache recordsets
  57          $ADODB_EXTENSION,   // ADODB extension installed
  58          $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
  59           $ADODB_FETCH_MODE;    // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
  60      
  61      //==============================================================================================    
  62      // GLOBAL SETUP
  63      //==============================================================================================    
  64      
  65      $ADODB_EXTENSION = defined('ADODB_EXTENSION');
  66      
  67      //********************************************************//
  68      /*
  69      Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
  70      Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
  71  
  72           0 = ignore empty fields. All empty fields in array are ignored.
  73          1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
  74          2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
  75          3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values.
  76      */
  77          define('ADODB_FORCE_IGNORE',0);
  78          define('ADODB_FORCE_NULL',1);
  79          define('ADODB_FORCE_EMPTY',2);
  80          define('ADODB_FORCE_VALUE',3);
  81      //********************************************************//
  82  
  83  
  84      if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
  85          
  86          define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
  87      
  88      // allow [ ] @ ` " and . in table names
  89          define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
  90      
  91      // prefetching used by oracle
  92          if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
  93      
  94      
  95      /*
  96      Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names.
  97      This currently works only with mssql, odbc, oci8po and ibase derived drivers.
  98      
  99           0 = assoc lowercase field names. $rs->fields['orderid']
 100          1 = assoc uppercase field names. $rs->fields['ORDERID']
 101          2 = use native-case field names. $rs->fields['OrderID']
 102      */
 103      
 104          define('ADODB_FETCH_DEFAULT',0);
 105          define('ADODB_FETCH_NUM',1);
 106          define('ADODB_FETCH_ASSOC',2);
 107          define('ADODB_FETCH_BOTH',3);
 108          
 109          if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100);
 110      
 111          // PHP's version scheme makes converting to numbers difficult - workaround
 112          $_adodb_ver = (float) PHP_VERSION;
 113          if ($_adodb_ver >= 5.0) {
 114              define('ADODB_PHPVER',0x5000);
 115          } else if ($_adodb_ver > 4.299999) { # 4.3
 116              define('ADODB_PHPVER',0x4300);
 117          } else if ($_adodb_ver > 4.199999) { # 4.2
 118              define('ADODB_PHPVER',0x4200);
 119          } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) {
 120              define('ADODB_PHPVER',0x4050);
 121          } else {
 122              define('ADODB_PHPVER',0x4000);
 123          }
 124      }
 125      
 126      //if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
 127  
 128      
 129      /**
 130           Accepts $src and $dest arrays, replacing string $data
 131      */
 132  	function ADODB_str_replace($src, $dest, $data)
 133      {
 134          if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data);
 135          
 136          $s = reset($src);
 137          $d = reset($dest);
 138          while ($s !== false) {
 139              $data = str_replace($s,$d,$data);
 140              $s = next($src);
 141              $d = next($dest);
 142          }
 143          return $data;
 144      }
 145      
 146  	function ADODB_Setup()
 147      {
 148      GLOBAL 
 149          $ADODB_vers,         // database version
 150          $ADODB_COUNTRECS,    // count number of records returned - slows down query
 151          $ADODB_CACHE_DIR,    // directory to cache recordsets
 152           $ADODB_FETCH_MODE,
 153          $ADODB_FORCE_TYPE;
 154          
 155          $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
 156          $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
 157  
 158  
 159          if (!isset($ADODB_CACHE_DIR)) {
 160              $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
 161          } else {
 162              // do not accept url based paths, eg. http:/ or ftp:/
 163              if (strpos($ADODB_CACHE_DIR,'://') !== false) 
 164                  die("Illegal path http:// or ftp://");
 165          }
 166          
 167              
 168          // Initialize random number generator for randomizing cache flushes
 169          srand(((double)microtime())*1000000);
 170          
 171          /**
 172           * ADODB version as a string.
 173           */
 174          $ADODB_vers = 'V4.90 8 June 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.';
 175      
 176          /**
 177           * Determines whether recordset->RecordCount() is used. 
 178           * Set to false for highest performance -- RecordCount() will always return -1 then
 179           * for databases that provide "virtual" recordcounts...
 180           */
 181          if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true; 
 182      }
 183      
 184      
 185      //==============================================================================================    
 186      // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
 187      //==============================================================================================    
 188      
 189      ADODB_Setup();
 190  
 191      //==============================================================================================    
 192      // CLASS ADOFieldObject
 193      //==============================================================================================    
 194      /**
 195       * Helper class for FetchFields -- holds info on a column
 196       */
 197      class ADOFieldObject { 
 198          var $name = '';
 199          var $max_length=0;
 200          var $type="";
 201  /*
 202          // additional fields by dannym... (danny_milo@yahoo.com)
 203          var $not_null = false; 
 204          // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
 205          // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
 206  
 207          var $has_default = false; // this one I have done only in mysql and postgres for now ... 
 208              // others to come (dannym)
 209          var $default_value; // default, if any, and supported. Check has_default first.
 210  */
 211      }
 212      
 213  
 214      
 215  	function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
 216      {
 217          //print "Errorno ($fn errno=$errno m=$errmsg) ";
 218          $thisConnection->_transOK = false;
 219          if ($thisConnection->_oldRaiseFn) {
 220              $fn = $thisConnection->_oldRaiseFn;
 221              $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
 222          }
 223      }
 224      
 225      //==============================================================================================    
 226      // CLASS ADOConnection
 227      //==============================================================================================    
 228      
 229      /**
 230       * Connection object. For connecting to databases, and executing queries.
 231       */ 
 232      class ADOConnection {
 233      //
 234      // PUBLIC VARS 
 235      //
 236      var $dataProvider = 'native';
 237      var $databaseType = '';        /// RDBMS currently in use, eg. odbc, mysql, mssql                    
 238      var $database = '';            /// Name of database to be used.    
 239      var $host = '';             /// The hostname of the database server    
 240      var $user = '';             /// The username which is used to connect to the database server. 
 241      var $password = '';         /// Password for the username. For security, we no longer store it.
 242      var $debug = false;         /// if set to true will output sql statements
 243      var $maxblobsize = 262144;     /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
 244      var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase    
 245      var $substr = 'substr';        /// substring operator
 246      var $length = 'length';        /// string length ofperator
 247      var $random = 'rand()';        /// random function
 248      var $upperCase = 'upper';        /// uppercase function
 249      var $fmtDate = "'Y-m-d'";    /// used by DBDate() as the default date format used by the database
 250      var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
 251      var $true = '1';             /// string that represents TRUE for a database
 252      var $false = '0';             /// string that represents FALSE for a database
 253      var $replaceQuote = "\\'";     /// string to use to replace quotes
 254      var $nameQuote = '"';        /// string to use to quote identifiers and names
 255      var $charSet=false;         /// character set to use - only for interbase, postgres and oci8
 256      var $metaDatabasesSQL = '';
 257      var $metaTablesSQL = '';
 258      var $uniqueOrderBy = false; /// All order by columns have to be unique
 259      var $emptyDate = '&nbsp;';
 260      var $emptyTimeStamp = '&nbsp;';
 261      var $lastInsID = false;
 262      //--
 263      var $hasInsertID = false;         /// supports autoincrement ID?
 264      var $hasAffectedRows = false;     /// supports affected rows for update/delete?
 265      var $hasTop = false;            /// support mssql/access SELECT TOP 10 * FROM TABLE
 266      var $hasLimit = false;            /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
 267      var $readOnly = false;             /// this is a readonly database - used by phpLens
 268      var $hasMoveFirst = false;  /// has ability to run MoveFirst(), scrolling backwards
 269      var $hasGenID = false;         /// can generate sequences using GenID();
 270      var $hasTransactions = true; /// has transactions
 271      //--
 272      var $genID = 0;             /// sequence id used by GenID();
 273      var $raiseErrorFn = false;     /// error function to call
 274      var $isoDates = false; /// accepts dates in ISO format
 275      var $cacheSecs = 3600; /// cache for 1 hour
 276  
 277      // memcache
 278      var $memCache = false; /// should we use memCache instead of caching in files
 279      var $memCacheHost; /// memCache host
 280      var $memCachePort = 11211; /// memCache port
 281      var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
 282  
 283      var $sysDate = false; /// name of function that returns the current date
 284      var $sysTimeStamp = false; /// name of function that returns the current timestamp
 285      var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
 286      
 287      var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
 288      var $numCacheHits = 0; 
 289      var $numCacheMisses = 0;
 290      var $pageExecuteCountRows = true;
 291      var $uniqueSort = false; /// indicates that all fields in order by must be unique
 292      var $leftOuter = false; /// operator to use for left outer join in WHERE clause
 293      var $rightOuter = false; /// operator to use for right outer join in WHERE clause
 294      var $ansiOuter = false; /// whether ansi outer join syntax supported
 295      var $autoRollback = false; // autoRollback on PConnect().
 296      var $poorAffectedRows = false; // affectedRows not working or unreliable
 297      
 298      var $fnExecute = false;
 299      var $fnCacheExecute = false;
 300      var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
 301      var $rsPrefix = "ADORecordSet_";
 302      
 303      var $autoCommit = true;     /// do not modify this yourself - actually private
 304      var $transOff = 0;             /// temporarily disable transactions
 305      var $transCnt = 0;             /// count of nested transactions
 306      
 307      var $fetchMode=false;
 308       //
 309       // PRIVATE VARS
 310       //
 311      var $_oldRaiseFn =  false;
 312      var $_transOK = null;
 313      var $_connectionID    = false;    /// The returned link identifier whenever a successful database connection is made.    
 314      var $_errorMsg = false;        /// A variable which was used to keep the returned last error message.  The value will
 315                                  /// then returned by the errorMsg() function    
 316      var $_errorCode = false;    /// Last error code, not guaranteed to be used - only by oci8                    
 317      var $_queryID = false;        /// This variable keeps the last created result link identifier
 318      
 319      var $_isPersistentConnection = false;    /// A boolean variable to state whether its a persistent connection or normal connection.    */
 320      var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
 321      var $_evalAll = false;
 322      var $_affected = false;
 323      var $_logsql = false;
 324      var $_transmode = ''; // transaction mode
 325      
 326      /**
 327       * Constructor
 328       */
 329  	function ADOConnection()            
 330      {
 331          die('Virtual Class -- cannot instantiate');
 332      }
 333      
 334  	function Version()
 335      {
 336      global $ADODB_vers;
 337      
 338          return (float) substr($ADODB_vers,1);
 339      }
 340      
 341      /**
 342          Get server version info...
 343          
 344          @returns An array with 2 elements: $arr['string'] is the description string, 
 345              and $arr[version] is the version (also a string).
 346      */
 347  	function ServerInfo()
 348      {
 349          return array('description' => '', 'version' => '');
 350      }
 351      
 352  	function IsConnected()
 353      {
 354          return !empty($this->_connectionID);
 355      }
 356      
 357  	function _findvers($str)
 358      {
 359          if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1];
 360          else return '';
 361      }
 362      
 363      /**
 364      * All error messages go through this bottleneck function.
 365      * You can define your own handler by defining the function name in ADODB_OUTP.
 366      */
 367  	function outp($msg,$newline=true)
 368      {
 369      global $ADODB_FLUSH,$ADODB_OUTP;
 370      
 371          if (defined('ADODB_OUTP')) {
 372              $fn = ADODB_OUTP;
 373              $fn($msg,$newline);
 374              return;
 375          } else if (isset($ADODB_OUTP)) {
 376              $fn = $ADODB_OUTP;
 377              $fn($msg,$newline);
 378              return;
 379          }
 380          
 381          if ($newline) $msg .= "<br>\n";
 382          
 383          if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg;
 384          else echo strip_tags($msg);
 385      
 386          
 387          if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan 
 388          
 389      }
 390      
 391  	function Time()
 392      {
 393          $rs =& $this->_Execute("select $this->sysTimeStamp");
 394          if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
 395          
 396          return false;
 397      }
 398      
 399      /**
 400       * Connect to database
 401       *
 402       * @param [argHostname]        Host to connect to
 403       * @param [argUsername]        Userid to login
 404       * @param [argPassword]        Associated password
 405       * @param [argDatabaseName]    database
 406       * @param [forceNew]        force new connection
 407       *
 408       * @return true or false
 409       */      
 410  	function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) 
 411      {
 412          if ($argHostname != "") $this->host = $argHostname;
 413          if ($argUsername != "") $this->user = $argUsername;
 414          if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
 415          if ($argDatabaseName != "") $this->database = $argDatabaseName;        
 416          
 417          $this->_isPersistentConnection = false;    
 418          if ($forceNew) {
 419              if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
 420          } else {
 421               if ($rez=$this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
 422          }
 423          if (isset($rez)) {
 424              $err = $this->ErrorMsg();
 425              if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
 426              $ret = false;
 427          } else {
 428              $err = "Missing extension for ".$this->dataProvider;
 429              $ret = 0;
 430          }
 431          if ($fn = $this->raiseErrorFn) 
 432              $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
 433          
 434          
 435          $this->_connectionID = false;
 436          if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
 437          return $ret;
 438      }    
 439      
 440  	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
 441      {
 442          return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
 443      }
 444      
 445      
 446      /**
 447       * Always force a new connection to database - currently only works with oracle
 448       *
 449       * @param [argHostname]        Host to connect to
 450       * @param [argUsername]        Userid to login
 451       * @param [argPassword]        Associated password
 452       * @param [argDatabaseName]    database
 453       *
 454       * @return true or false
 455       */      
 456  	function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") 
 457      {
 458          return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
 459      }
 460      
 461      /**
 462       * Establish persistent connect to database
 463       *
 464       * @param [argHostname]        Host to connect to
 465       * @param [argUsername]        Userid to login
 466       * @param [argPassword]        Associated password
 467       * @param [argDatabaseName]    database
 468       *
 469       * @return return true or false
 470       */    
 471  	function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
 472      {
 473          if (defined('ADODB_NEVER_PERSIST')) 
 474              return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
 475          
 476          if ($argHostname != "") $this->host = $argHostname;
 477          if ($argUsername != "") $this->user = $argUsername;
 478          if ($argPassword != "") $this->password = $argPassword;
 479          if ($argDatabaseName != "") $this->database = $argDatabaseName;        
 480              
 481          $this->_isPersistentConnection = true;    
 482          if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
 483          if (isset($rez)) {
 484              $err = $this->ErrorMsg();
 485              if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
 486              $ret = false;
 487          } else {
 488              $err = "Missing extension for ".$this->dataProvider;
 489              $ret = 0;
 490          }
 491          if ($fn = $this->raiseErrorFn) {
 492              $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
 493          }
 494          
 495          $this->_connectionID = false;
 496          if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
 497          return $ret;
 498      }
 499  
 500      // Format date column in sql string given an input format that understands Y M D
 501  	function SQLDate($fmt, $col=false)
 502      {    
 503          if (!$col) $col = $this->sysDate;
 504          return $col; // child class implement
 505      }
 506      
 507      /**
 508       * Should prepare the sql statement and return the stmt resource.
 509       * For databases that do not support this, we return the $sql. To ensure
 510       * compatibility with databases that do not support prepare:
 511       *
 512       *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
 513       *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
 514       *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
 515       *
 516       * @param sql    SQL to send to database
 517       *
 518       * @return return FALSE, or the prepared statement, or the original sql if
 519       *             if the database does not support prepare.
 520       *
 521       */    
 522  	function Prepare($sql)
 523      {
 524          return $sql;
 525      }
 526      
 527      /**
 528       * Some databases, eg. mssql require a different function for preparing
 529       * stored procedures. So we cannot use Prepare().
 530       *
 531       * Should prepare the stored procedure  and return the stmt resource.
 532       * For databases that do not support this, we return the $sql. To ensure
 533       * compatibility with databases that do not support prepare:
 534       *
 535       * @param sql    SQL to send to database
 536       *
 537       * @return return FALSE, or the prepared statement, or the original sql if
 538       *             if the database does not support prepare.
 539       *
 540       */    
 541  	function PrepareSP($sql,$param=true)
 542      {
 543          return $this->Prepare($sql,$param);
 544      }
 545      
 546      /**
 547      * PEAR DB Compat
 548      */
 549  	function Quote($s)
 550      {
 551          return $this->qstr($s,false);
 552      }
 553      
 554      /**
 555       Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
 556      */
 557  	function QMagic($s)
 558      {
 559          return $this->qstr($s,get_magic_quotes_gpc());
 560      }
 561  
 562      function q(&$s)
 563      {
 564          #if (!empty($this->qNull)) if ($s == 'null') return $s;
 565          $s = $this->qstr($s,false);
 566      }
 567      
 568      /**
 569      * PEAR DB Compat - do not use internally. 
 570      */
 571  	function ErrorNative()
 572      {
 573          return $this->ErrorNo();
 574      }
 575  
 576      
 577     /**
 578      * PEAR DB Compat - do not use internally. 
 579      */
 580  	function nextId($seq_name)
 581      {
 582          return $this->GenID($seq_name);
 583      }
 584  
 585      /**
 586      *     Lock a row, will escalate and lock the table if row locking not supported
 587      *    will normally free the lock at the end of the transaction
 588      *
 589      *  @param $table    name of table to lock
 590      *  @param $where    where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
 591      */
 592  	function RowLock($table,$where)
 593      {
 594          return false;
 595      }
 596      
 597  	function CommitLock($table)
 598      {
 599          return $this->CommitTrans();
 600      }
 601      
 602  	function RollbackLock($table)
 603      {
 604          return $this->RollbackTrans();
 605      }
 606      
 607      /**
 608      * PEAR DB Compat - do not use internally. 
 609      *
 610      * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
 611      *     for easy porting :-)
 612      *
 613      * @param mode    The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
 614      * @returns        The previous fetch mode
 615      */
 616  	function SetFetchMode($mode)
 617      {    
 618          $old = $this->fetchMode;
 619          $this->fetchMode = $mode;
 620          
 621          if ($old === false) {
 622          global $ADODB_FETCH_MODE;
 623              return $ADODB_FETCH_MODE;
 624          }
 625          return $old;
 626      }
 627      
 628  
 629      /**
 630      * PEAR DB Compat - do not use internally. 
 631      */
 632      function &Query($sql, $inputarr=false)
 633      {
 634          $rs = &$this->Execute($sql, $inputarr);
 635          if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
 636          return $rs;
 637      }
 638  
 639      
 640      /**
 641      * PEAR DB Compat - do not use internally
 642      */
 643      function &LimitQuery($sql, $offset, $count, $params=false)
 644      {
 645          $rs = &$this->SelectLimit($sql, $count, $offset, $params); 
 646          if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
 647          return $rs;
 648      }
 649  
 650      
 651      /**
 652      * PEAR DB Compat - do not use internally
 653      */
 654  	function Disconnect()
 655      {
 656          return $this->Close();
 657      }
 658      
 659      /*
 660           Returns placeholder for parameter, eg.
 661           $DB->Param('a')
 662           
 663           will return ':a' for Oracle, and '?' for most other databases...
 664           
 665           For databases that require positioned params, eg $1, $2, $3 for postgresql,
 666               pass in Param(false) before setting the first parameter.
 667      */
 668  	function Param($name,$type='C')
 669      {
 670          return '?';
 671      }
 672      
 673      /*
 674          InParameter and OutParameter are self-documenting versions of Parameter().
 675      */
 676  	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
 677      {
 678          return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
 679      }
 680      
 681      /*
 682      */
 683  	function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
 684      {
 685          return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
 686      
 687      }
 688  
 689      
 690      /* 
 691      Usage in oracle
 692          $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
 693          $db->Parameter($stmt,$id,'myid');
 694          $db->Parameter($stmt,$group,'group',64);
 695          $db->Execute();
 696          
 697          @param $stmt Statement returned by Prepare() or PrepareSP().
 698          @param $var PHP variable to bind to
 699          @param $name Name of stored procedure variable name to bind to.
 700          @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
 701          @param [$maxLen] Holds an maximum length of the variable.
 702          @param [$type] The data type of $var. Legal values depend on driver.
 703  
 704      */
 705  	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
 706      {
 707          return false;
 708      }
 709      
 710      
 711  	function IgnoreErrors($saveErrs=false)
 712      {
 713          if (!$saveErrs) {
 714              $saveErrs = array($this->raiseErrorFn,$this->_transOK);
 715              $this->raiseErrorFn = false;
 716              return $saveErrs;
 717          } else {
 718              $this->raiseErrorFn = $saveErrs[0];
 719              $this->_transOK = $saveErrs[1];
 720          }
 721      }
 722      
 723      /**
 724          Improved method of initiating a transaction. Used together with CompleteTrans().
 725          Advantages include:
 726          
 727          a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
 728             Only the outermost block is treated as a transaction.<br>
 729          b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
 730          c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
 731             are disabled, making it backward compatible.
 732      */
 733  	function StartTrans($errfn = 'ADODB_TransMonitor')
 734      {
 735          if ($this->transOff > 0) {
 736              $this->transOff += 1;
 737              return;
 738          }
 739          
 740          $this->_oldRaiseFn = $this->raiseErrorFn;
 741          $this->raiseErrorFn = $errfn;
 742          $this->_transOK = true;
 743          
 744          if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
 745          $this->BeginTrans();
 746          $this->transOff = 1;
 747      }
 748      
 749      
 750      /**
 751          Used together with StartTrans() to end a transaction. Monitors connection
 752          for sql errors, and will commit or rollback as appropriate.
 753          
 754          @autoComplete if true, monitor sql errors and commit and rollback as appropriate, 
 755          and if set to false force rollback even if no SQL error detected.
 756          @returns true on commit, false on rollback.
 757      */
 758  	function CompleteTrans($autoComplete = true)
 759      {
 760          if ($this->transOff > 1) {
 761              $this->transOff -= 1;
 762              return true;
 763          }
 764          $this->raiseErrorFn = $this->_oldRaiseFn;
 765          
 766          $this->transOff = 0;
 767          if ($this->_transOK && $autoComplete) {
 768              if (!$this->CommitTrans()) {
 769                  $this->_transOK = false;
 770                  if ($this->debug) ADOConnection::outp("Smart Commit failed");
 771              } else
 772                  if ($this->debug) ADOConnection::outp("Smart Commit occurred");
 773          } else {
 774              $this->_transOK = false;
 775              $this->RollbackTrans();
 776              if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred");
 777          }
 778          
 779          return $this->_transOK;
 780      }
 781      
 782      /*
 783          At the end of a StartTrans/CompleteTrans block, perform a rollback.
 784      */
 785  	function FailTrans()
 786      {
 787          if ($this->debug) 
 788              if ($this->transOff == 0) {
 789                  ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
 790              } else {
 791                  ADOConnection::outp("FailTrans was called");
 792                  adodb_backtrace();
 793              }
 794          $this->_transOK = false;
 795      }
 796      
 797      /**
 798          Check if transaction has failed, only for Smart Transactions.
 799      */
 800  	function HasFailedTrans()
 801      {
 802          if ($this->transOff > 0) return $this->_transOK == false;
 803          return false;
 804      }
 805      
 806      /**
 807       * Execute SQL 
 808       *
 809       * @param sql        SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
 810       * @param [inputarr]    holds the input data to bind to. Null elements will be set to null.
 811       * @return         RecordSet or false
 812       */
 813      function &Execute($sql,$inputarr=false) 
 814      {
 815          if ($this->fnExecute) {
 816              $fn = $this->fnExecute;
 817              $ret =& $fn($this,$sql,$inputarr);
 818              if (isset($ret)) return $ret;
 819          }
 820          if ($inputarr) {
 821              if (!is_array($inputarr)) $inputarr = array($inputarr);
 822              
 823              $element0 = reset($inputarr);
 824              # is_object check because oci8 descriptors can be passed in
 825              $array_2d = is_array($element0) && !is_object(reset($element0));
 826              //remove extra memory copy of input -mikefedyk
 827              unset($element0);
 828              
 829              if (!is_array($sql) && !$this->_bindInputArray) {
 830                  $sqlarr = explode('?',$sql);
 831                      
 832                  if (!$array_2d) $inputarr = array($inputarr);
 833                  foreach($inputarr as $arr) {
 834                      $sql = ''; $i = 0;
 835                      //Use each() instead of foreach to reduce memory usage -mikefedyk
 836                      while(list(, $v) = each($arr)) {
 837                          $sql .= $sqlarr[$i];
 838                          // from Ron Baldwin <ron.baldwin#sourceprose.com>
 839                          // Only quote string types    
 840                          $typ = gettype($v);
 841                          if ($typ == 'string')
 842                              //New memory copy of input created here -mikefedyk
 843                              $sql .= $this->qstr($v);
 844                          else if ($typ == 'double')
 845                              $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
 846                          else if ($typ == 'boolean')
 847                              $sql .= $v ? $this->true : $this->false;
 848                          else if ($typ == 'object') {
 849                              if (method_exists($v, '__toString')) $sql .= $this->qstr($v->__toString());
 850                              else $sql .= $this->qstr((string) $v);
 851                          } else if ($v === null)
 852                              $sql .= 'NULL';
 853                          else
 854                              $sql .= $v;
 855                          $i += 1;
 856                      }
 857                      if (isset($sqlarr[$i])) {
 858                          $sql .= $sqlarr[$i];
 859                          if ($i+1 != sizeof($sqlarr)) ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql));
 860                      } else if ($i != sizeof($sqlarr))    
 861                          ADOConnection::outp( "Input array does not match ?: ".htmlspecialchars($sql));
 862          
 863                      $ret =& $this->_Execute($sql);
 864                      if (!$ret) return $ret;
 865                  }    
 866              } else {
 867                  if ($array_2d) {
 868                      if (is_string($sql))
 869                          $stmt = $this->Prepare($sql);
 870                      else
 871                          $stmt = $sql;
 872                          
 873                      foreach($inputarr as $arr) {
 874                          $ret =& $this->_Execute($stmt,$arr);
 875                          if (!$ret) return $ret;
 876                      }
 877                  } else {
 878                      $ret =& $this->_Execute($sql,$inputarr);
 879                  }
 880              }
 881          } else {
 882              $ret =& $this->_Execute($sql,false);
 883          }
 884  
 885          return $ret;
 886      }
 887      
 888      
 889      function &_Execute($sql,$inputarr=false)
 890      {
 891          if ($this->debug) {
 892              global $ADODB_INCLUDED_LIB;
 893              if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
 894              $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
 895          } else {
 896              $this->_queryID = @$this->_query($sql,$inputarr);
 897          }
 898          
 899          /************************
 900          // OK, query executed
 901          *************************/
 902  
 903          if ($this->_queryID === false) { // error handling if query fails
 904              if ($this->debug == 99) adodb_backtrace(true,5);    
 905              $fn = $this->raiseErrorFn;
 906              if ($fn) {
 907                  $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
 908              } 
 909              $false = false;
 910              return $false;
 911          } 
 912          
 913          if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead
 914              $rs =& new ADORecordSet_empty();
 915              return $rs;
 916          }
 917          
 918          // return real recordset from select statement
 919          $rsclass = $this->rsPrefix.$this->databaseType;
 920          $rs = new $rsclass($this->_queryID,$this->fetchMode);
 921          $rs->connection = &$this; // Pablo suggestion
 922          $rs->Init();
 923          if (is_array($sql)) $rs->sql = $sql[0];
 924          else $rs->sql = $sql;
 925          if ($rs->_numOfRows <= 0) {
 926          global $ADODB_COUNTRECS;
 927              if ($ADODB_COUNTRECS) {
 928                  if (!$rs->EOF) { 
 929                      $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql));
 930                      $rs->_queryID = $this->_queryID;
 931                  } else
 932                      $rs->_numOfRows = 0;
 933              }
 934          }
 935          return $rs;
 936      }
 937  
 938  	function CreateSequence($seqname='adodbseq',$startID=1)
 939      {
 940          if (empty($this->_genSeqSQL)) return false;
 941          return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
 942      }
 943  
 944  	function DropSequence($seqname='adodbseq')
 945      {
 946          if (empty($this->_dropSeqSQL)) return false;
 947          return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
 948      }
 949  
 950      /**
 951       * Generates a sequence id and stores it in $this->genID;
 952       * GenID is only available if $this->hasGenID = true;
 953       *
 954       * @param seqname        name of sequence to use
 955       * @param startID        if sequence does not exist, start at this ID
 956       * @return        0 if not supported, otherwise a sequence id
 957       */
 958  	function GenID($seqname='adodbseq',$startID=1)
 959      {
 960          if (!$this->hasGenID) {
 961              return 0; // formerly returns false pre 1.60
 962          }
 963          
 964          $getnext = sprintf($this->_genIDSQL,$seqname);
 965          
 966          $holdtransOK = $this->_transOK;
 967          
 968          $save_handler = $this->raiseErrorFn;
 969          $this->raiseErrorFn = '';
 970          @($rs = $this->Execute($getnext));
 971          $this->raiseErrorFn = $save_handler;
 972          
 973          if (!$rs) {
 974              $this->_transOK = $holdtransOK; //if the status was ok before reset
 975              $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
 976              $rs = $this->Execute($getnext);
 977          }
 978          if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
 979          else $this->genID = 0; // false
 980      
 981          if ($rs) $rs->Close();
 982  
 983          return $this->genID;
 984      }    
 985  
 986      /**
 987       * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
 988       * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
 989       * @return  the last inserted ID. Not all databases support this.
 990       */ 
 991  	function Insert_ID($table='',$column='')
 992      {
 993          if ($this->_logsql && $this->lastInsID) return $this->lastInsID;
 994          if ($this->hasInsertID) return $this->_insertid($table,$column);
 995          if ($this->debug) {
 996              ADOConnection::outp( '<p>Insert_ID error</p>');
 997              adodb_backtrace();
 998          }
 999          return false;
1000      }
1001  
1002  
1003      /**
1004       * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
1005       *
1006       * @return  the last inserted ID. All databases support this. But aware possible
1007       * problems in multiuser environments. Heavy test this before deploying.
1008       */ 
1009  	function PO_Insert_ID($table="", $id="") 
1010      {
1011         if ($this->hasInsertID){
1012             return $this->Insert_ID($table,$id);
1013         } else {
1014             return $this->GetOne("SELECT MAX($id) FROM $table");
1015         }
1016      }
1017  
1018      /**
1019      * @return # rows affected by UPDATE/DELETE
1020      */ 
1021  	function Affected_Rows()
1022      {
1023          if ($this->hasAffectedRows) {
1024              if ($this->fnExecute === 'adodb_log_sql') {
1025                  if ($this->_logsql && $this->_affected !== false) return $this->_affected;
1026              }
1027              $val = $this->_affectedrows();
1028              return ($val < 0) ? false : $val;
1029          }
1030                    
1031          if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1032          return false;
1033      }
1034      
1035      
1036      /**
1037       * @return  the last error message
1038       */
1039  	function ErrorMsg()
1040      {
1041          if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1042          else return '';
1043      }
1044      
1045      
1046      /**
1047       * @return the last error number. Normally 0 means no error.
1048       */
1049  	function ErrorNo() 
1050      {
1051          return ($this->_errorMsg) ? -1 : 0;
1052      }
1053      
1054  	function MetaError($err=false)
1055      {
1056          include_once (ADODB_DIR."/adodb-error.inc.php");
1057          if ($err === false) $err = $this->ErrorNo();
1058          return adodb_error($this->dataProvider,$this->databaseType,$err);
1059      }
1060      
1061  	function MetaErrorMsg($errno)
1062      {
1063          include_once (ADODB_DIR."/adodb-error.inc.php");
1064          return adodb_errormsg($errno);
1065      }
1066      
1067      /**
1068       * @returns an array with the primary key columns in it.
1069       */
1070  	function MetaPrimaryKeys($table, $owner=false)
1071      {
1072      // owner not used in base class - see oci8
1073          $p = array();
1074          $objs =& $this->MetaColumns($table);
1075          if ($objs) {
1076              foreach($objs as $v) {
1077                  if (!empty($v->primary_key))
1078                      $p[] = $v->name;
1079              }
1080          }
1081          if (sizeof($p)) return $p;
1082          if (function_exists('ADODB_VIEW_PRIMARYKEYS'))
1083              return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1084          return false;
1085      }
1086      
1087      /**
1088       * @returns assoc array where keys are tables, and values are foreign keys
1089       */
1090  	function MetaForeignKeys($table, $owner=false, $upper=false)
1091      {
1092          return false;
1093      }
1094      /**
1095       * Choose a database to connect to. Many databases do not support this.
1096       *
1097       * @param dbName     is the name of the database to select
1098       * @return         true or false
1099       */
1100  	function SelectDB($dbName) 
1101      {return false;}
1102      
1103      
1104      /**
1105      * Will select, getting rows from $offset (1-based), for $nrows. 
1106      * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1107      * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1108      * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1109      * eg. 
1110      *  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1111      *  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1112      *
1113      * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1114      * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1115      *
1116      * @param sql
1117      * @param [offset]    is the row to start calculations from (1-based)
1118      * @param [nrows]        is the number of rows to get
1119      * @param [inputarr]    array of bind variables
1120      * @param [secs2cache]        is a private parameter only used by jlim
1121      * @return        the recordset ($rs->databaseType == 'array')
1122       */
1123      function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
1124      {
1125          if ($this->hasTop && $nrows > 0) {
1126          // suggested by Reinhard Balling. Access requires top after distinct 
1127           // Informix requires first before distinct - F Riosa
1128              $ismssql = (strpos($this->databaseType,'mssql') !== false);
1129              if ($ismssql) $isaccess = false;
1130              else $isaccess = (strpos($this->databaseType,'access') !== false);
1131              
1132              if ($offset <=     0) {
1133                  
1134                      // access includes ties in result
1135                      if ($isaccess) {
1136                          $sql = preg_replace(
1137                          '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1138  
1139                          if ($secs2cache != 0) {
1140                              $ret =& $this->CacheExecute($secs2cache, $sql,$inputarr);
1141                          } else {
1142                              $ret =& $this->Execute($sql,$inputarr);
1143                          }
1144                          return $ret; // PHP5 fix
1145                      } else if ($ismssql){
1146                          $sql = preg_replace(
1147                          '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1148                      } else {
1149                          $sql = preg_replace(
1150                          '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1151                      }
1152              } else {
1153                  $nn = $nrows + $offset;
1154                  if ($isaccess || $ismssql) {
1155                      $sql = preg_replace(
1156                      '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1157                  } else {
1158                      $sql = preg_replace(
1159                      '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1160                  }
1161              }
1162          }
1163          
1164          // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
1165          // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1166          global $ADODB_COUNTRECS;
1167          
1168          $savec = $ADODB_COUNTRECS;
1169          $ADODB_COUNTRECS = false;
1170              
1171          if ($offset>0){
1172              if ($secs2cache != 0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1173              else $rs = &$this->Execute($sql,$inputarr);
1174          } else {
1175              if ($secs2cache != 0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1176              else $rs = &$this->Execute($sql,$inputarr);
1177          }
1178          $ADODB_COUNTRECS = $savec;
1179          if ($rs && !$rs->EOF) {
1180              $rs =& $this->_rs2rs($rs,$nrows,$offset);
1181          }
1182          //print_r($rs);
1183          return $rs;
1184      }
1185      
1186      /**
1187      * Create serializable recordset. Breaks rs link to connection.
1188      *
1189      * @param rs            the recordset to serialize
1190      */
1191      function &SerializableRS(&$rs)
1192      {
1193          $rs2 =& $this->_rs2rs($rs);
1194          $ignore = false;
1195          $rs2->connection =& $ignore;
1196          
1197          return $rs2;
1198      }
1199      
1200      /**
1201      * Convert database recordset to an array recordset
1202      * input recordset's cursor should be at beginning, and
1203      * old $rs will be closed.
1204      *
1205      * @param rs            the recordset to copy
1206      * @param [nrows]      number of rows to retrieve (optional)
1207      * @param [offset]     offset by number of rows (optional)
1208      * @return             the new recordset
1209      */
1210      function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true)
1211      {
1212          if (! $rs) {
1213              $false = false;
1214              return $false;
1215          }
1216          $dbtype = $rs->databaseType;
1217          if (!$dbtype) {
1218              $rs = &$rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1219              return $rs;
1220          }
1221          if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1222              $rs->MoveFirst();
1223              $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1224              return $rs;
1225          }
1226          $flds = array();
1227          for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1228              $flds[] = $rs->FetchField($i);
1229          }
1230  
1231          $arr =& $rs->GetArrayLimit($nrows,$offset);
1232          //print_r($arr);
1233          if ($close) $rs->Close();
1234          
1235          $arrayClass = $this->arrayClass;
1236          
1237          $rs2 = new $arrayClass();
1238          $rs2->connection = &$this;
1239          $rs2->sql = $rs->sql;
1240          $rs2->dataProvider = $this->dataProvider;
1241          $rs2->InitArrayFields($arr,$flds);
1242          $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1243          return $rs2;
1244      }
1245      
1246      /*
1247      * Return all rows. Compat with PEAR DB
1248      */
1249      function &GetAll($sql, $inputarr=false)
1250      {
1251          $arr =& $this->GetArray($sql,$inputarr);
1252          return $arr;
1253      }
1254      
1255      function &GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false)
1256      {
1257          $rs =& $this->Execute($sql, $inputarr);
1258          if (!$rs) {
1259              $false = false;
1260              return $false;
1261          }
1262          $arr =& $rs->GetAssoc($force_array,$first2cols);
1263          return $arr;
1264      }
1265      
1266      function &CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false)
1267      {
1268          if (!is_numeric($secs2cache)) {
1269              $first2cols = $force_array;
1270              $force_array = $inputarr;
1271          }
1272          $rs =& $this->CacheExecute($secs2cache, $sql, $inputarr);
1273          if (!$rs) {
1274              $false = false;
1275              return $false;
1276          }
1277          $arr =& $rs->GetAssoc($force_array,$first2cols);
1278          return $arr;
1279      }
1280      
1281      /**
1282      * Return first element of first row of sql statement. Recordset is disposed
1283      * for you.
1284      *
1285      * @param sql            SQL statement
1286      * @param [inputarr]        input bind array
1287      */
1288  	function GetOne($sql,$inputarr=false)
1289      {
1290      global $ADODB_COUNTRECS;
1291          $crecs = $ADODB_COUNTRECS;
1292          $ADODB_COUNTRECS = false;
1293          
1294          $ret = false;
1295          $rs = &$this->Execute($sql,$inputarr);
1296          if ($rs) {    
1297              if (!$rs->EOF) $ret = reset($rs->fields);
1298              $rs->Close();
1299          }
1300          $ADODB_COUNTRECS = $crecs;
1301          return $ret;
1302      }
1303      
1304  	function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
1305      {
1306          $ret = false;
1307          $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1308          if ($rs) {        
1309              if (!$rs->EOF) $ret = reset($rs->fields);
1310              $rs->Close();
1311          } 
1312          
1313          return $ret;
1314      }
1315      
1316  	function GetCol($sql, $inputarr = false, $trim = false)
1317      {
1318            $rv = false;
1319            $rs = &$this->Execute($sql, $inputarr);
1320            if ($rs) {
1321              $rv = array();
1322                 if ($trim) {
1323                  while (!$rs->EOF) {
1324                      $rv[] = trim(reset($rs->fields));
1325                      $rs->MoveNext();
1326                     }
1327              } else {
1328                  while (!$rs->EOF) {
1329                      $rv[] = reset($rs->fields);
1330                      $rs->MoveNext();
1331                     }
1332              }
1333                 $rs->Close();
1334            }
1335            return $rv;
1336      }
1337      
1338  	function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false)
1339      {
1340            $rv = false;
1341            $rs = &$this->CacheExecute($secs, $sql, $inputarr);
1342            if ($rs) {
1343              if ($trim) {
1344                  while (!$rs->EOF) {
1345                      $rv[] = trim(reset($rs->fields));
1346                      $rs->MoveNext();
1347                     }
1348              } else {
1349                  while (!$rs->EOF) {
1350                      $rv[] = reset($rs->fields);
1351                      $rs->MoveNext();
1352                     }
1353              }
1354                 $rs->Close();
1355            }
1356            return $rv;
1357      }
1358      
1359      function &Transpose(&$rs)
1360      {
1361          $rs2 =& $this->_rs2rs($rs);
1362          $false = false;
1363          if (!$rs2) return $false;
1364          
1365          $rs2->_transpose();
1366          return $rs2;
1367      }
1368   
1369      /*
1370          Calculate the offset of a date for a particular database and generate
1371              appropriate SQL. Useful for calculating future/past dates and storing
1372              in a database.
1373              
1374          If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1375      */
1376  	function OffsetDate($dayFraction,$date=false)
1377      {        
1378          if (!$date) $date = $this->sysDate;
1379          return  '('.$date.'+'.$dayFraction.')';
1380      }
1381      
1382      
1383      /**
1384      *
1385      * @param sql            SQL statement
1386      * @param [inputarr]        input bind array
1387      */
1388      function &GetArray($sql,$inputarr=false)
1389      {
1390      global $ADODB_COUNTRECS;
1391          
1392          $savec = $ADODB_COUNTRECS;
1393          $ADODB_COUNTRECS = false;
1394          $rs =& $this->Execute($sql,$inputarr);
1395          $ADODB_COUNTRECS = $savec;
1396          if (!$rs) 
1397              if (defined('ADODB_PEAR')) {
1398                  $cls = ADODB_PEAR_Error();
1399                  return $cls;
1400              } else {
1401                  $false = false;
1402                  return $false;
1403              }
1404          $arr =& $rs->GetArray();
1405          $rs->Close();
1406          return $arr;
1407      }
1408      
1409      function &CacheGetAll($secs2cache,$sql=false,$inputarr=false)
1410      {
1411          return $this->CacheGetArray($secs2cache,$sql,$inputarr);
1412      }
1413      
1414      function &CacheGetArray($secs2cache,$sql=false,$inputarr=false)
1415      {
1416      global $ADODB_COUNTRECS;
1417          
1418          $savec = $ADODB_COUNTRECS;
1419          $ADODB_COUNTRECS = false;
1420          $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1421          $ADODB_COUNTRECS = $savec;
1422          
1423          if (!$rs) 
1424              if (defined('ADODB_PEAR')) {
1425                  $cls = ADODB_PEAR_Error();
1426                  return $cls;
1427              } else {
1428                  $false = false;
1429                  return $false;
1430              }
1431          $arr =& $rs->GetArray();
1432          $rs->Close();
1433          return $arr;
1434      }
1435      
1436      
1437      
1438      /**
1439      * Return one row of sql statement. Recordset is disposed for you.
1440      *
1441      * @param sql            SQL statement
1442      * @param [inputarr]        input bind array
1443      */
1444      function &GetRow($sql,$inputarr=false)
1445      {
1446      global $ADODB_COUNTRECS;
1447          $crecs = $ADODB_COUNTRECS;
1448          $ADODB_COUNTRECS = false;
1449          
1450          $rs =& $this->Execute($sql,$inputarr);
1451          
1452          $ADODB_COUNTRECS = $crecs;
1453          if ($rs) {
1454              if (!$rs->EOF) $arr = $rs->fields;
1455              else $arr = array();
1456              $rs->Close();
1457              return $arr;
1458          }
1459          
1460          $false = false;
1461          return $false;
1462      }
1463      
1464      function &CacheGetRow($secs2cache,$sql=false,$inputarr=false)
1465      {
1466          $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1467          if ($rs) {
1468              $arr = false;
1469              if (!$rs->EOF) $arr = $rs->fields;
1470              $rs->Close();
1471              return $arr;
1472          }
1473          $false = false;
1474          return $false;
1475      }
1476      
1477      /**
1478      * Insert or replace a single record. Note: this is not the same as MySQL's replace. 
1479      * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1480      * Also note that no table locking is done currently, so it is possible that the
1481      * record be inserted twice by two programs...
1482      *
1483      * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1484      *
1485      * $table        table name
1486      * $fieldArray    associative array of data (you must quote strings yourself).
1487      * $keyCol        the primary key field name or if compound key, array of field names
1488      * autoQuote        set to true to use a hueristic to quote strings. Works with nulls and numbers
1489      *                    but does not work with dates nor SQL functions.
1490      * has_autoinc    the primary key is an auto-inc field, so skip in insert.
1491      *
1492      * Currently blob replace not supported
1493      *
1494      * returns 0 = fail, 1 = update, 2 = insert 
1495      */
1496      
1497  	function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false)
1498      {
1499          global $ADODB_INCLUDED_LIB;
1500          if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
1501          
1502          return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1503      }
1504      
1505      
1506      /**
1507      * Will select, getting rows from $offset (1-based), for $nrows. 
1508      * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1509      * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1510      * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1511      * eg. 
1512      *  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1513      *  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1514      *
1515      * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1516      *
1517      * @param [secs2cache]    seconds to cache data, set to 0 to force query. This is optional
1518      * @param sql
1519      * @param [offset]    is the row to start calculations from (1-based)
1520      * @param [nrows]    is the number of rows to get
1521      * @param [inputarr]    array of bind variables
1522      * @return        the recordset ($rs->databaseType == 'array')
1523       */
1524      function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false)
1525      {    
1526          if (!is_numeric($secs2cache)) {
1527              if ($sql === false) $sql = -1;
1528              if ($offset == -1) $offset = false;
1529                                        // sql,    nrows, offset,inputarr
1530              $rs =& $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1531          } else {
1532              if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
1533              $rs =& $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1534          }
1535          return $rs;
1536      }
1537      
1538      
1539      /**
1540      * Flush cached recordsets that match a particular $sql statement. 
1541      * If $sql == false, then we purge all files in the cache.
1542       */
1543      
1544      /**
1545     * Flush cached recordsets that match a particular $sql statement. 
1546     * If $sql == false, then we purge all files in the cache.
1547      */
1548  	function CacheFlush($sql=false,$inputarr=false)
1549      {
1550      global $ADODB_CACHE_DIR;
1551      
1552          if ($this->memCache) {
1553          global $ADODB_INCLUDED_MEMCACHE;
1554          
1555              $key = false;
1556              if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
1557              if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true);
1558              FlushMemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug);
1559              return;
1560          }
1561      
1562          if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1563           /*if (strncmp(PHP_OS,'WIN',3) === 0)
1564              $dir = str_replace('/', '\\', $ADODB_CACHE_DIR);
1565           else */
1566              $dir = $ADODB_CACHE_DIR;
1567              
1568           if ($this->debug) {
1569              ADOConnection::outp( "CacheFlush: $dir<br><pre>\n", $this->_dirFlush($dir),"</pre>");
1570           } else {
1571              $this->_dirFlush($dir);
1572           }
1573           return;
1574        } 
1575        
1576        global $ADODB_INCLUDED_CSV;
1577        if (empty($ADODB_INCLUDED_CSV)) include (ADODB_DIR.'/adodb-csvlib.inc.php');
1578        
1579        $f = $this->_gencachename($sql.serialize($inputarr),false);
1580        adodb_write_file($f,''); // is adodb_write_file needed?
1581        if (!@unlink($f)) {
1582           if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1583        }
1584     }
1585     
1586     /**
1587     * Private function to erase all of the files and subdirectories in a directory.
1588     *
1589     * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
1590     * Note: $kill_top_level is used internally in the function to flush subdirectories.
1591     */
1592     function _dirFlush($dir, $kill_top_level = false) {
1593        if(!$dh = @opendir($dir)) return;
1594        
1595        while (($obj = readdir($dh))) {
1596           if($obj=='.' || $obj=='..')
1597              continue;
1598              
1599           if (!@unlink($dir.'/'.$obj))
1600                $this->_dirFlush($dir.'/'.$obj, true);
1601        }
1602        if ($kill_top_level === true)
1603           @rmdir($dir);
1604        return true;
1605     }
1606     
1607     
1608  	function xCacheFlush($sql=false,$inputarr=false)
1609      {
1610      global $ADODB_CACHE_DIR;
1611      
1612          if ($this->memCache) {
1613              global $ADODB_INCLUDED_MEMCACHE;
1614              $key = false;
1615              if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
1616              if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true);
1617              flushmemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug);
1618              return;
1619          }
1620  
1621          if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1622              if (strncmp(PHP_OS,'WIN',3) === 0) {
1623                  $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
1624              } else {
1625                  //$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f';
1626                  $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/'; 
1627                  // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
1628              }
1629              if ($this->debug) {
1630                  ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>");
1631              } else {
1632                  exec($cmd);
1633              }
1634              return;
1635          } 
1636          
1637          global $ADODB_INCLUDED_CSV;
1638          if (empty($ADODB_INCLUDED_CSV)) include (ADODB_DIR.'/adodb-csvlib.inc.php');
1639          
1640          $f = $this->_gencachename($sql.serialize($inputarr),false);
1641          adodb_write_file($f,''); // is adodb_write_file needed?
1642          if (!@unlink($f)) {
1643              if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1644          }
1645      }
1646      
1647      /**
1648      * Private function to generate filename for caching.
1649      * Filename is generated based on:
1650      *
1651      *  - sql statement
1652      *  - database type (oci8, ibase, ifx, etc)
1653      *  - database name
1654      *  - userid
1655      *  - setFetchMode (adodb 4.23)
1656      *
1657      * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). 
1658      * Assuming that we can have 50,000 files per directory with good performance, 
1659      * then we can scale to 12.8 million unique cached recordsets. Wow!
1660       */
1661  	function _gencachename($sql,$createdir,$memcache=false)
1662      {
1663      global $ADODB_CACHE_DIR;
1664      static $notSafeMode;
1665          
1666          if ($this->fetchMode === false) { 
1667          global $ADODB_FETCH_MODE;
1668              $mode = $ADODB_FETCH_MODE;
1669          } else {
1670              $mode = $this->fetchMode;
1671          }
1672          $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1673          if ($memcache) return $m;
1674          
1675          if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode');
1676          $dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR;
1677              
1678          if ($createdir && $notSafeMode && !file_exists($dir)) {
1679              $oldu = umask(0);
1680              if (!mkdir($dir,0771)) 
1681                  if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
1682              umask($oldu);
1683          }
1684          return $dir.'/adodb_'.$m.'.cache';
1685      }
1686      
1687      
1688      /**
1689       * Execute SQL, caching recordsets.
1690       *
1691       * @param [secs2cache]    seconds to cache data, set to 0 to force query. 
1692       *                      This is an optional parameter.
1693       * @param sql        SQL statement to execute
1694       * @param [inputarr]    holds the input data  to bind to
1695       * @return         RecordSet or false
1696       */
1697      function &CacheExecute($secs2cache,$sql=false,$inputarr=false)
1698      {
1699  
1700              
1701          if (!is_numeric($secs2cache)) {
1702              $inputarr = $sql;
1703              $sql = $secs2cache;
1704              $secs2cache = $this->cacheSecs;
1705          }
1706          
1707          if (is_array($sql)) {
1708              $sqlparam = $sql;
1709              $sql = $sql[0];
1710          } else
1711              $sqlparam = $sql;
1712              
1713          if ($this->memCache) {
1714              global $ADODB_INCLUDED_MEMCACHE;
1715              if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
1716              $md5file = $this->_gencachename($sql.serialize($inputarr),false,true);
1717          } else {
1718          global $ADODB_INCLUDED_CSV;
1719              if (empty($ADODB_INCLUDED_CSV)) include (ADODB_DIR.'/adodb-csvlib.inc.php');
1720              $md5file = $this->_gencachename($sql.serialize($inputarr),true);
1721          }
1722  
1723          $err = '';
1724          
1725          if ($secs2cache > 0){
1726              if ($this->memCache)
1727                  $rs = &getmemCache($md5file,$err,$secs2cache, $this->memCacheHost, $this->memCachePort);
1728              else
1729                  $rs = &csv2rs($md5file,$err,$secs2cache,$this->arrayClass);
1730              $this->numCacheHits += 1;
1731          } else {
1732              $err='Timeout 1';
1733              $rs = false;
1734              $this->numCacheMisses += 1;
1735          }
1736          if (!$rs) {
1737          // no cached rs found
1738              if ($this->debug) {
1739                  if (get_magic_quotes_runtime() && !$this->memCache) {
1740                      ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
1741                  }
1742                  if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
1743              }
1744              
1745              $rs = &$this->Execute($sqlparam,$inputarr);
1746  
1747              if ($rs && $this->memCache) {
1748                  $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
1749                  if(!putmemCache($md5file, $rs, $this->memCacheHost, $this->memCachePort, $this->memCacheCompress, $this->debug)) {
1750                      if ($fn = $this->raiseErrorFn)
1751                          $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
1752                      if ($this->debug) ADOConnection::outp( " Cache write error");
1753                  }
1754              } else
1755              if ($rs) {
1756                  $eof = $rs->EOF;
1757                  $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
1758                  $txt = _rs2serialize($rs,false,$sql); // serialize
1759          
1760                  if (!adodb_write_file($md5file,$txt,$this->debug)) {
1761                      if ($fn = $this->raiseErrorFn) {
1762                          $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
1763                      }
1764                      if ($this->debug) ADOConnection::outp( " Cache write error");
1765                  }
1766                  if ($rs->EOF && !$eof) {
1767                      $rs->MoveFirst();
1768                      //$rs = &csv2rs($md5file,$err);        
1769                      $rs->connection = &$this; // Pablo suggestion
1770                  }  
1771                  
1772              } else
1773              if (!$this->memCache)
1774                  @unlink($md5file);
1775          } else {
1776              $this->_errorMsg = '';
1777              $this->_errorCode = 0;
1778              
1779              if ($this->fnCacheExecute) {
1780                  $fn = $this->fnCacheExecute;
1781                  $fn($this, $secs2cache, $sql, $inputarr);
1782              }
1783          // ok, set cached object found
1784              $rs->connection = &$this; // Pablo suggestion
1785              if ($this->debug){ 
1786                      
1787                  $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1788                  $ttl = $rs->timeCreated + $secs2cache - time();
1789                  $s = is_array($sql) ? $sql[0] : $sql;
1790                  if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>';
1791                  
1792                  ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
1793              }
1794          }
1795          return $rs;
1796      }
1797      
1798      
1799      /* 
1800          Similar to PEAR DB's autoExecute(), except that 
1801          $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
1802          If $mode == 'UPDATE', then $where is compulsory as a safety measure.
1803          
1804          $forceUpdate means that even if the data has not changed, perform update.
1805       */
1806      function& AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false) 
1807      {
1808          $false = false;
1809          $sql = 'SELECT * FROM '.$table;  
1810          if ($where!==FALSE) $sql .= ' WHERE '.$where;
1811          else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) {
1812              ADOConnection::outp('AutoExecute: Illegal mode=UPDATE with empty WHERE clause');
1813              return $false;
1814          }
1815  
1816          $rs =& $this->SelectLimit($sql,1);
1817          if (!$rs) return $false; // table does not exist
1818          $rs->tableName = $table;
1819          
1820          switch((string) $mode) {
1821          case 'UPDATE':
1822          case '2':
1823              $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
1824              break;
1825          case 'INSERT':
1826          case '1':
1827              $sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
1828              break;
1829          default:
1830              ADOConnection::outp("AutoExecute: Unknown mode=$mode");
1831              return $false;
1832          }
1833          $ret = false;
1834          if ($sql) $ret = $this->Execute($sql);
1835          if ($ret) $ret = true;
1836          return $ret;
1837      }
1838      
1839      
1840      /**
1841       * Generates an Update Query based on an existing recordset.
1842       * $arrFields is an associative array of fields with the value
1843       * that should be assigned.
1844       *
1845       * Note: This function should only be used on a recordset
1846       *       that is run against a single table and sql should only 
1847       *         be a simple select stmt with no groupby/orderby/limit
1848       *
1849       * "Jonathan Younger" <jyounger@unilab.com>
1850         */
1851  	function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null)
1852      {
1853          global $ADODB_INCLUDED_LIB;
1854  
1855          //********************************************************//
1856          //This is here to maintain compatibility
1857          //with older adodb versions. Sets force type to force nulls if $forcenulls is set.
1858          if (!isset($force)) {
1859                  global $ADODB_FORCE_TYPE;
1860                  $force = $ADODB_FORCE_TYPE;
1861          }
1862          //********************************************************//
1863  
1864          if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
1865          return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
1866      }
1867  
1868      /**
1869       * Generates an Insert Query based on an existing recordset.
1870       * $arrFields is an associative array of fields with the value
1871       * that should be assigned.
1872       *
1873       * Note: This function should only be used on a recordset
1874       *       that is run against a single table.
1875         */
1876  	function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null)
1877      {    
1878          global $ADODB_INCLUDED_LIB;
1879          if (!isset($force)) {
1880              global $ADODB_FORCE_TYPE;
1881              $force = $ADODB_FORCE_TYPE;
1882              
1883          }
1884          if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
1885          return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
1886      }
1887      
1888  
1889      /**
1890      * Update a blob column, given a where clause. There are more sophisticated
1891      * blob handling functions that we could have implemented, but all require
1892      * a very complex API. Instead we have chosen something that is extremely
1893      * simple to understand and use. 
1894      *
1895      * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
1896      *
1897      * Usage to update a $blobvalue which has a primary key blob_id=1 into a 
1898      * field blobtable.blobcolumn:
1899      *
1900      *    UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
1901      *
1902      * Insert example:
1903      *
1904      *    $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1905      *    $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1906      */
1907      
1908  	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
1909      {
1910          return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1911      }
1912  
1913      /**
1914      * Usage:
1915      *    UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
1916      *    
1917      *    $blobtype supports 'BLOB' and 'CLOB'
1918      *
1919      *    $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1920      *    $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
1921      */
1922  	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
1923      {
1924          $fd = fopen($path,'rb');
1925          if ($fd === false) return false;
1926          $val = fread($fd,filesize($path));
1927          fclose($fd);
1928          return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
1929      }
1930      
1931  	function BlobDecode($blob)
1932      {
1933          return $blob;
1934      }
1935      
1936  	function BlobEncode($blob)
1937      {
1938          return $blob;
1939      }
1940      
1941  	function SetCharSet($charset)
1942      {
1943          return false;
1944      }
1945      
1946  	function IfNull( $field, $ifNull ) 
1947      {
1948          return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
1949      }
1950      
1951  	function LogSQL($enable=true)
1952      {
1953          include_once (ADODB_DIR.'/adodb-perf.inc.php');
1954          
1955          if ($enable) $this->fnExecute = 'adodb_log_sql';
1956          else $this->fnExecute = false;
1957          
1958          $old = $this->_logsql;    
1959          $this->_logsql = $enable;
1960          if ($enable && !$old) $this->_affected = false;
1961          return $old;
1962      }
1963      
1964  	function GetCharSet()
1965      {
1966          return false;
1967      }
1968      
1969      /**
1970      * Usage:
1971      *    UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
1972      *
1973      *    $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
1974      *    $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
1975      */
1976  	function UpdateClob($table,$column,$val,$where)
1977      {
1978          return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
1979      }
1980      
1981      // not the fastest implementation - quick and dirty - jlim
1982      // for best performance, use the actual $rs->MetaType().
1983  	function MetaType($t,$len=-1,$fieldobj=false)
1984      {
1985          
1986          if (empty($this->_metars)) {
1987              $rsclass = $this->rsPrefix.$this->databaseType;
1988              $this->_metars =& new $rsclass(false,$this->fetchMode); 
1989              $this->_metars->connection =& $this;
1990          }
1991          return $this->_metars->MetaType($t,$len,$fieldobj);
1992      }
1993      
1994      
1995      /**
1996      *  Change the SQL connection locale to a specified locale.
1997      *  This is used to get the date formats written depending on the client locale.
1998      */
1999  	function SetDateLocale($locale = 'En')
2000      {
2001          $this->locale = $locale;
2002          switch (strtoupper($locale))
2003          {
2004              case 'EN':
2005                  $this->fmtDate="'Y-m-d'";
2006                  $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2007                  break;
2008                  
2009              case 'US':
2010                  $this->fmtDate = "'m-d-Y'";
2011                  $this->fmtTimeStamp = "'m-d-Y H:i:s'";
2012                  break;
2013                  
2014              case 'NL':
2015              case 'FR':
2016              case 'RO':
2017              case 'IT':
2018                  $this->fmtDate="'d-m-Y'";
2019                  $this->fmtTimeStamp = "'d-m-Y H:i:s'";
2020                  break;
2021                  
2022              case 'GE':
2023                  $this->fmtDate="'d.m.Y'";
2024                  $this->fmtTimeStamp = "'d.m.Y H:i:s'";
2025                  break;
2026                  
2027              default:
2028                  $this->fmtDate="'Y-m-d'";
2029                  $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2030                  break;
2031          }
2032      }
2033  
2034      function &GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false)
2035      {
2036      global $_ADODB_ACTIVE_DBS;
2037      
2038          $save = $this->SetFetchMode(ADODB_FETCH_NUM);
2039          if (empty($whereOrderBy)) $whereOrderBy = '1=1';
2040          $rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr);
2041          $this->SetFetchMode($save);
2042          
2043          $false = false;
2044          
2045          if ($rows === false) {    
2046              return $false;
2047          }
2048          
2049          
2050          if (!isset($_ADODB_ACTIVE_DBS)) {
2051              include(ADODB_DIR.'/adodb-active-record.inc.php');
2052          }    
2053          if (!class_exists($class)) {
2054              ADOConnection::outp("Unknown class $class in GetActiveRcordsClass()");
2055              return $false;
2056          }
2057          $arr = array();
2058          foreach($rows as $row) {
2059          
2060              $obj =& new $class($table,$primkeyArr,$this);
2061              if ($obj->ErrorMsg()){
2062                  $this->_errorMsg = $obj->ErrorMsg();
2063                  return $false;
2064              }
2065              $obj->Set($row);
2066              $arr[] =& $obj;
2067          }
2068          return $arr;
2069      }
2070      
2071      function &GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
2072      {
2073          $arr =& $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2074          return $arr;
2075      }
2076      
2077      /**
2078       * Close Connection
2079       */
2080  	function Close()
2081      {
2082          $rez = $this->_close();
2083          $this->_connectionID = false;
2084          return $rez;
2085      }
2086      
2087      /**
2088       * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2089       *
2090       * @return true if succeeded or false if database does not support transactions
2091       */
2092  	function BeginTrans() {return false;}
2093      
2094      /* set transaction mode */
2095  	function SetTransactionMode( $transaction_mode ) 
2096      {
2097          $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2098          $this->_transmode  = $transaction_mode;
2099      }
2100  /*
2101  http://msdn2.microsoft.com/en-US/ms173763.aspx
2102  http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2103  http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2104  http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2105  */
2106  	function MetaTransaction($mode,$db)
2107      {
2108          $mode = strtoupper($mode);
2109          $mode = str_replace('ISOLATION LEVEL ','',$mode);
2110          
2111          switch($mode) {
2112  
2113          case 'READ UNCOMMITTED':
2114              switch($db) { 
2115              case 'oci8':
2116              case 'oracle':
2117                  return 'ISOLATION LEVEL READ COMMITTED';
2118              default:
2119                  return 'ISOLATION LEVEL READ UNCOMMITTED';
2120              }
2121              break;
2122                      
2123          case 'READ COMMITTED':
2124                  return 'ISOLATION LEVEL READ COMMITTED';
2125              break;
2126              
2127          case 'REPEATABLE READ':
2128              switch($db) {
2129              case 'oci8':
2130              case 'oracle':
2131                  return 'ISOLATION LEVEL SERIALIZABLE';
2132              default:
2133                  return 'ISOLATION LEVEL REPEATABLE READ';
2134              }
2135              break;
2136              
2137          case 'SERIALIZABLE':
2138                  return 'ISOLATION LEVEL SERIALIZABLE';
2139              break;
2140              
2141          default:
2142              return $mode;
2143          }
2144      }
2145      
2146      /**
2147       * If database does not support transactions, always return true as data always commited
2148       *
2149       * @param $ok  set to false to rollback transaction, true to commit
2150       *
2151       * @return true/false.
2152       */
2153  	function CommitTrans($ok=true) 
2154      { return true;}
2155      
2156      
2157      /**
2158       * If database does not support transactions, rollbacks always fail, so return false
2159       *
2160       * @return true/false.
2161       */
2162  	function RollbackTrans() 
2163      { return false;}
2164  
2165  
2166      /**
2167       * return the databases that the driver can connect to. 
2168       * Some databases will return an empty array.
2169       *
2170       * @return an array of database names.
2171       */
2172  		function MetaDatabases() 
2173          {
2174          global $ADODB_FETCH_MODE;
2175          
2176              if ($this->metaDatabasesSQL) {
2177                  $save = $ADODB_FETCH_MODE; 
2178                  $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 
2179                  
2180                  if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2181                  
2182                  $arr = $this->GetCol($this->metaDatabasesSQL);
2183                  if (isset($savem)) $this->SetFetchMode($savem);
2184                  $ADODB_FETCH_MODE = $save; 
2185              
2186                  return $arr;
2187              }
2188              
2189              return false;
2190          }
2191      
2192          
2193      /**
2194       * @param ttype can either be 'VIEW' or 'TABLE' or false. 
2195       *         If false, both views and tables are returned.
2196       *        "VIEW" returns only views
2197       *        "TABLE" returns only tables
2198       * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2199       * @param mask  is the input mask - only supported by oci8 and postgresql
2200       *
2201       * @return  array of tables for current database.
2202       */ 
2203      function &MetaTables($ttype=false,$showSchema=false,$mask=false) 
2204      {
2205      global $ADODB_FETCH_MODE;
2206      
2207          
2208          $false = false;
2209          if ($mask) {
2210              return $false;
2211          }
2212          if ($this->metaTablesSQL) {
2213              $save = $ADODB_FETCH_MODE; 
2214              $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 
2215              
2216              if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2217              
2218              $rs = $this->Execute($this->metaTablesSQL);
2219              if (isset($savem)) $this->SetFetchMode($savem);
2220              $ADODB_FETCH_MODE = $save; 
2221              
2222              if ($rs === false) return $false;
2223              $arr =& $rs->GetArray();
2224              $arr2 = array();
2225              
2226              if ($hast = ($ttype && isset($arr[0][1]))) { 
2227                  $showt = strncmp($ttype,'T',1);
2228              }
2229              
2230              for ($i=0; $i < sizeof($arr); $i++) {
2231                  if ($hast) {
2232                      if ($showt == 0) {
2233                          if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]);
2234                      } else {
2235                          if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]);
2236                      }
2237                  } else
2238                      $arr2[] = trim($arr[$i][0]);
2239              }
2240              $rs->Close();
2241              return $arr2;
2242          }
2243          return $false;
2244      }
2245      
2246      
2247  	function _findschema(&$table,&$schema)
2248      {
2249          if (!$schema && ($at = strpos($table,'.')) !== false) {
2250              $schema = substr($table,0,$at);
2251              $table = substr($table,$at+1);
2252          }
2253      }
2254      
2255      /**
2256       * List columns in a database as an array of ADOFieldObjects. 
2257       * See top of file for definition of object.
2258       *
2259       * @param $table    table name to query
2260       * @param $normalize    makes table name case-insensitive (required by some databases)
2261       * @schema is optional database schema to use - not supported by all databases.
2262       *
2263       * @return  array of ADOFieldObjects for current table.
2264       */
2265      function &MetaColumns($table,$normalize=true) 
2266      {
2267      global $ADODB_FETCH_MODE;
2268          
2269          $false = false;
2270          
2271          if (!empty($this->metaColumnsSQL)) {
2272          
2273              $schema = false;
2274              $this->_findschema($table,$schema);
2275          
2276              $save = $ADODB_FETCH_MODE;
2277              $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2278              if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2279              $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2280              if (isset($savem)) $this->SetFetchMode($savem);
2281              $ADODB_FETCH_MODE = $save;
2282              if ($rs === false || $rs->EOF) return $false;
2283  
2284              $retarr = array();
2285              while (!$rs->EOF) { //print_r($rs->fields);
2286                  $fld = new ADOFieldObject();
2287                  $fld->name = $rs->fields[0];
2288                  $fld->type = $rs->fields[1];
2289                  if (isset($rs->fields[3]) && $rs->fields[3]) {
2290                      if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
2291                      $fld->scale = $rs->fields[4];
2292                      if ($fld->scale>0) $fld->max_length += 1;
2293                  } else
2294                      $fld->max_length = $rs->fields[2];
2295                      
2296                  if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;    
2297                  else $retarr[strtoupper($fld->name)] = $fld;
2298                  $rs->MoveNext();
2299              }
2300              $rs->Close();
2301              return $retarr;    
2302          }
2303          return $false;
2304      }
2305      
2306      /**
2307        * List indexes on a table as an array.
2308        * @param table  table name to query
2309        * @param primary true to only show primary keys. Not actually used for most databases
2310        *
2311        * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2312        
2313           Array (
2314              [name_of_index] => Array
2315                (
2316                [unique] => true or false
2317                [columns] => Array
2318                (
2319                    [0] => firstname
2320                    [1] => lastname
2321                )
2322          )        
2323        */
2324       function &MetaIndexes($table, $primary = false, $owner = false)
2325       {
2326               $false = false;
2327              return $false;
2328       }
2329  
2330      /**
2331       * List columns names in a table as an array. 
2332       * @param table    table name to query
2333       *
2334       * @return  array of column names for current table.
2335       */ 
2336      function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) 
2337      {
2338          $objarr =& $this->MetaColumns($table);
2339          if (!is_array($objarr)) {
2340              $false = false;
2341              return $false;
2342          }
2343          $arr = array();
2344          if ($numIndexes) {
2345              $i = 0;
2346              if ($useattnum) {
2347                  foreach($objarr as $v) 
2348                      $arr[$v->attnum] = $v->name;
2349                  
2350              } else
2351                  foreach($objarr as $v) $arr[$i++] = $v->name;
2352          } else
2353              foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2354          
2355          return $arr;
2356      }
2357              
2358      /**
2359       * Different SQL databases used different methods to combine strings together.
2360       * This function provides a wrapper. 
2361       * 
2362       * param s    variable number of string parameters
2363       *
2364       * Usage: $db->Concat($str1,$str2);
2365       * 
2366       * @return concatenated string
2367       */      
2368  	function Concat()
2369      {    
2370          $arr = func_get_args();
2371          return implode($this->concat_operator, $arr);
2372      }
2373      
2374      
2375      /**
2376       * Converts a date "d" to a string that the database can understand.
2377       *
2378       * @param d    a date in Unix date time format.
2379       *
2380       * @return  date string in database date format
2381       */
2382  	function DBDate($d)
2383      {
2384          if (empty($d) && $d !== 0) return 'null';
2385  
2386          if (is_string($d) && !is_numeric($d)) {
2387              if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
2388              if ($this->isoDates) return "'$d'";
2389              $d = ADOConnection::UnixDate($d);
2390          }
2391  
2392          return adodb_date($this->fmtDate,$d);
2393      }
2394      
2395  	function BindDate($d)
2396      {
2397          $d = $this->DBDate($d);
2398          if (strncmp($d,"'",1)) return $d;
2399          
2400          return substr($d,1,strlen($d)-2);
2401      }
2402      
2403  	function BindTimeStamp($d)
2404      {
2405          $d = $this->DBTimeStamp($d);
2406          if (strncmp($d,"'",1)) return $d;
2407          
2408          return substr($d,1,strlen($d)-2);
2409      }
2410      
2411      
2412      /**
2413       * Converts a timestamp "ts" to a string that the database can understand.
2414       *
2415       * @param ts    a timestamp in Unix date time format.
2416       *
2417       * @return  timestamp string in database timestamp format
2418       */
2419  	function DBTimeStamp($ts)
2420      {
2421          if (empty($ts) && $ts !== 0) return 'null';
2422  
2423          # strlen(14) allows YYYYMMDDHHMMSS format
2424          if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) 
2425              return adodb_date($this->fmtTimeStamp,$ts);
2426          
2427          if ($ts === 'null') return $ts;
2428          if ($this->isoDates && strlen($ts) !== 14) return "'$ts'";
2429          
2430          $ts = ADOConnection::UnixTimeStamp($ts);
2431          return adodb_date($this->fmtTimeStamp,$ts);
2432      }
2433      
2434      /**
2435       * Also in ADORecordSet.
2436       * @param $v is a date string in YYYY-MM-DD format
2437       *
2438       * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2439       */
2440  	function UnixDate($v)
2441      {
2442          if (is_object($v)) {
2443          // odbtp support
2444          //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2445              return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2446          }
2447      
2448          if (is_numeric($v) && strlen($v) !== 8) return $v;
2449          if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", 
2450              ($v), $rr)) return false;
2451  
2452          if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
2453          // h-m-s-MM-DD-YY
2454          return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2455      }
2456      
2457  
2458      /**
2459       * Also in ADORecordSet.
2460       * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2461       *
2462       * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2463       */
2464  	function UnixTimeStamp($v)
2465      {
2466          if (is_object($v)) {
2467          // odbtp support
2468          //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2469              return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2470          }
2471          
2472          if (!preg_match( 
2473              "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", 
2474              ($v), $rr)) return false;
2475              
2476          if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
2477      
2478          // h-m-s-MM-DD-YY
2479          if (!isset($rr[5])) return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2480          return  @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2481      }
2482      
2483      /**
2484       * Also in ADORecordSet.
2485       *
2486       * Format database date based on user defined format.
2487       *
2488       * @param v      is the character date in YYYY-MM-DD format, returned by database
2489       * @param fmt     is the format to apply to it, using date()
2490       *
2491       * @return a date formated as user desires
2492       */
2493       
2494  	function UserDate($v,$fmt='Y-m-d',$gmt=false)
2495      {
2496          $tt = $this->UnixDate($v);
2497  
2498          // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2499          if (($tt === false || $tt == -1) && $v != false) return $v;
2500          else if ($tt == 0) return $this->emptyDate;
2501          else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2502          }
2503          
2504          return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2505      
2506      }
2507      
2508          /**
2509       *
2510       * @param v      is the character timestamp in YYYY-MM-DD hh:mm:ss format
2511       * @param fmt     is the format to apply to it, using date()
2512       *
2513       * @return a timestamp formated as user desires
2514       */
2515  	function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false)
2516      {
2517          if (!isset($v)) return $this->emptyTimeStamp;
2518          # strlen(14) allows YYYYMMDDHHMMSS format
2519          if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2520          $tt = $this->UnixTimeStamp($v);
2521          // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2522          if (($tt === false || $tt == -1) && $v != false) return $v;
2523          if ($tt == 0) return $this->emptyTimeStamp;
2524          return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2525      }
2526      
2527  	function escape($s,$magic_quotes=false)
2528      {
2529          return $this->addq($s,$magic_quotes);
2530      }
2531      
2532      /**
2533      * Quotes a string, without prefixing nor appending quotes. 
2534      */
2535  	function addq($s,$magic_quotes=false)
2536      {
2537          if (!$magic_quotes) {
2538          
2539              if ($this->replaceQuote[0] == '\\'){
2540                  // only since php 4.0.5
2541                  $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2542                  //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2543              }
2544              return  str_replace("'",$this->replaceQuote,$s);
2545          }
2546          
2547          // undo magic quotes for "
2548          $s = str_replace('\\"','"',$s);
2549          
2550          if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
2551              return $s;
2552          else {// change \' to '' for sybase/mssql
2553              $s = str_replace('\\\\','\\',$s);
2554              return str_replace("\\'",$this->replaceQuote,$s);
2555          }
2556      }
2557      
2558      /**
2559       * Correctly quotes a string so that all strings are escaped. We prefix and append
2560       * to the string single-quotes.
2561       * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
2562       * 
2563       * @param s            the string to quote
2564       * @param [magic_quotes]    if $s is GET/POST var, set to get_magic_quotes_gpc().
2565       *                This undoes the stupidity of magic quotes for GPC.
2566       *
2567       * @return  quoted string to be sent back to database
2568       */
2569  	function qstr($s,$magic_quotes=false)
2570      {    
2571          if (!$magic_quotes) {
2572          
2573              if ($this->replaceQuote[0] == '\\'){
2574                  // only since php 4.0.5
2575                  $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2576                  //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2577              }
2578              return  "'".str_replace("'",$this->replaceQuote,$s)."'";
2579          }
2580          
2581          // undo magic quotes for "
2582          $s = str_replace('\\"','"',$s);
2583          
2584          if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
2585              return "'$s'";
2586          else {// change \' to '' for sybase/mssql
2587              $s = str_replace('\\\\','\\',$s);
2588              return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2589          }
2590      }
2591      
2592      
2593      /**
2594      * Will select the supplied $page number from a recordset, given that it is paginated in pages of 
2595      * $nrows rows per page. It also saves two boolean values saying if the given page is the first 
2596      * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2597      *
2598      * See readme.htm#ex8 for an example of usage.
2599      *
2600      * @param sql
2601      * @param nrows        is the number of rows per page to get
2602      * @param page        is the page number to get (1-based)
2603      * @param [inputarr]    array of bind variables
2604      * @param [secs2cache]        is a private parameter only used by jlim
2605      * @return        the recordset ($rs->databaseType == 'array')
2606      *
2607      * NOTE: phpLens uses a different algorithm and does not use PageExecute().
2608      *
2609      */
2610      function &PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) 
2611      {
2612          global $ADODB_INCLUDED_LIB;
2613          if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
2614          if ($this->pageExecuteCountRows) $rs =& _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2615          else $rs =& _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2616          return $rs;
2617      }
2618      
2619          
2620      /**
2621      * Will select the supplied $page number from a recordset, given that it is paginated in pages of 
2622      * $nrows rows per page. It also saves two boolean values saying if the given page is the first 
2623      * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2624      *
2625      * @param secs2cache    seconds to cache data, set to 0 to force query
2626      * @param sql
2627      * @param nrows        is the number of rows per page to get
2628      * @param page        is the page number to get (1-based)
2629      * @param [inputarr]    array of bind variables
2630      * @return        the recordset ($rs->databaseType == 'array')
2631      */
2632      function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) 
2633      {
2634          /*switch($this->dataProvider) {
2635          case 'postgres':
2636          case 'mysql': 
2637              break;
2638          default: $secs2cache = 0; break;
2639          }*/
2640          $rs =& $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
2641          return $rs;
2642      }
2643  
2644  } // end class ADOConnection
2645      
2646      
2647      
2648      //==============================================================================================    
2649      // CLASS ADOFetchObj
2650      //==============================================================================================    
2651          
2652      /**
2653      * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
2654      */
2655      class ADOFetchObj {
2656      };
2657      
2658      //==============================================================================================    
2659      // CLASS ADORecordSet_empty
2660      //==============================================================================================    
2661      
2662      /**
2663      * Lightweight recordset when there are no records to be returned
2664      */
2665      class ADORecordSet_empty
2666      {
2667          var $dataProvider = 'empty';
2668          var $databaseType = false;
2669          var $EOF = true;
2670          var $_numOfRows = 0;
2671          var $fields = false;
2672          var $connection = false;
2673  		function RowCount() {return 0;}
2674  		function RecordCount() {return 0;}
2675  		function PO_RecordCount(){return 0;}
2676  		function Close(){return true;}
2677  		function FetchRow() {return false;}
2678  		function FieldCount(){ return 0;}
2679  		function Init() {}
2680      }
2681      
2682      //==============================================================================================    
2683      // DATE AND TIME FUNCTIONS
2684      //==============================================================================================    
2685      if (!defined('ADODB_DATE_VERSION')) include (ADODB_DIR.'/adodb-time.inc.php');
2686      
2687      //==============================================================================================    
2688      // CLASS ADORecordSet
2689      //==============================================================================================    
2690  
2691      if (PHP_VERSION < 5) include_once (ADODB_DIR.'/adodb-php4.inc.php');
2692      else include_once (ADODB_DIR.'/adodb-iterator.inc.php');
2693     /**
2694       * RecordSet class that represents the dataset returned by the database.
2695       * To keep memory overhead low, this class holds only the current row in memory.
2696       * No prefetching of data is done, so the RecordCount() can return -1 ( which
2697       * means recordcount not known).
2698       */
2699      class ADORecordSet extends ADODB_BASE_RS {
2700      /*
2701       * public variables    
2702       */
2703      var $dataProvider = "native";
2704      var $fields = false;     /// holds the current row data
2705      var $blobSize = 100;     /// any varchar/char field this size or greater is treated as a blob
2706                              /// in other words, we use a text area for editing.
2707      var $canSeek = false;     /// indicates that seek is supported
2708      var $sql;                 /// sql text
2709      var $EOF = false;        /// Indicates that the current record position is after the last record in a Recordset object. 
2710      
2711      var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
2712      var $emptyDate = '&nbsp;'; /// what to display when $time==0
2713      var $debug = false;
2714      var $timeCreated=0;     /// datetime in Unix format rs created -- for cached recordsets
2715  
2716      var $bind = false;         /// used by Fields() to hold array - should be private?
2717      var $fetchMode;            /// default fetch mode
2718      var $connection = false; /// the parent connection
2719      /*
2720       *    private variables    
2721       */
2722      var $_numOfRows = -1;    /** number of rows, or -1 */
2723      var $_numOfFields = -1;    /** number of fields in recordset */
2724      var $_queryID = -1;        /** This variable keeps the result link identifier.    */
2725      var $_currentRow = -1;    /** This variable keeps the current row in the Recordset.    */
2726      var $_closed = false;     /** has recordset been closed */
2727      var $_inited = false;     /** Init() should only be called once */
2728      var $_obj;                 /** Used by FetchObj */
2729      var $_names;            /** Used by FetchObj */
2730      
2731      var $_currentPage = -1;    /** Added by Iván Oliva to implement recordset pagination */
2732      var $_atFirstPage = false;    /** Added by Iván Oliva to implement recordset pagination */
2733      var $_atLastPage = false;    /** Added by Iván Oliva to implement recordset pagination */
2734      var $_lastPageNo = -1; 
2735      var $_maxRecordCount = 0;
2736      var $datetime = false;
2737      
2738      /**
2739       * Constructor
2740       *
2741       * @param queryID      this is the queryID returned by ADOConnection->_query()
2742       *
2743       */
2744  	function ADORecordSet($queryID) 
2745      {
2746          $this->_queryID = $queryID;
2747      }
2748      
2749      
2750      
2751  	function Init()
2752      {
2753          if ($this->_inited) return;
2754          $this->_inited = true;
2755          if ($this->_queryID) @$this->_initrs();
2756          else {
2757              $this->_numOfRows = 0;
2758              $this->_numOfFields = 0;
2759          }
2760          if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
2761              
2762              $this->_currentRow = 0;
2763              if ($this->EOF = ($this->_fetch() === false)) {
2764                  $this->_numOfRows = 0; // _numOfRows could be -1
2765              }
2766          } else {
2767              $this->EOF = true;
2768          }
2769      }
2770      
2771      
2772      /**
2773       * Generate a SELECT tag string from a recordset, and return the string.
2774       * If the recordset has 2 cols, we treat the 1st col as the containing 
2775       * the text to display to the user, and 2nd col as the return value. Default
2776       * strings are compared with the FIRST column.
2777       *
2778       * @param name          name of SELECT tag
2779       * @param [defstr]        the value to hilite. Use an array for multiple hilites for listbox.
2780       * @param [blank1stItem]    true to leave the 1st item in list empty
2781       * @param [multiple]        true for listbox, false for popup
2782       * @param [size]        #rows to show for listbox. not used by popup
2783       * @param [selectAttr]        additional attributes to defined for SELECT tag.
2784       *                useful for holding javascript onChange='...' handlers.
2785       & @param [compareFields0]    when we have 2 cols in recordset, we compare the defstr with 
2786       *                column 0 (1st col) if this is true. This is not documented.
2787       *
2788       * @return HTML
2789       *
2790       * changes by glen.davies@cce.ac.nz to support multiple hilited items
2791       */
2792  	function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
2793              $size=0, $selectAttr='',$compareFields0=true)
2794      {
2795          global $ADODB_INCLUDED_LIB;
2796          if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
2797          return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
2798              $size, $selectAttr,$compareFields0);
2799      }
2800      
2801  
2802      
2803      /**
2804       * Generate a SELECT tag string from a recordset, and return the string.
2805       * If the recordset has 2 cols, we treat the 1st col as the containing 
2806       * the text to display to the user, and 2nd col as the return value. Default
2807       * strings are compared with the SECOND column.
2808       *
2809       */
2810  	function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')    
2811      {
2812          return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
2813              $size, $selectAttr,false);
2814      }
2815      
2816      /*
2817          Grouped Menu
2818      */
2819  	function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
2820              $size=0, $selectAttr='')
2821      {
2822          global $ADODB_INCLUDED_LIB;
2823          if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
2824          return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
2825              $size, $selectAttr,false);
2826      }
2827  
2828      /**
2829       * return recordset as a 2-dimensional array.
2830       *
2831       * @param [nRows]  is the number of rows to return. -1 means every row.
2832       *
2833       * @return an array indexed by the rows (0-based) from the recordset
2834       */
2835      function &GetArray($nRows = -1) 
2836      {
2837      global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
2838          $results = adodb_getall($this,$nRows);
2839          return $results;
2840      }
2841          $results = array();
2842          $cnt = 0;
2843          while (!$this->EOF && $nRows != $cnt) {
2844              $results[] = $this->fields;
2845              $this->MoveNext();
2846              $cnt++;
2847          }
2848          return $results;
2849      }
2850      
2851      function &GetAll($nRows = -1)
2852      {
2853          $arr =& $this->GetArray($nRows);
2854          return $arr;
2855      }
2856      
2857      /*
2858      * Some databases allow multiple recordsets to be returned. This function
2859      * will return true if there is a next recordset, or false if no more.
2860      */
2861  	function NextRecordSet()
2862      {
2863          return false;
2864      }
2865      
2866      /**
2867       * return recordset as a 2-dimensional array. 
2868       * Helper function for ADOConnection->SelectLimit()
2869       *
2870       * @param offset    is the row to start calculations from (1-based)
2871       * @param [nrows]    is the number of rows to return
2872       *
2873       * @return an array indexed by the rows (0-based) from the recordset
2874       */
2875      function &GetArrayLimit($nrows,$offset=-1) 
2876      {    
2877          if ($offset <= 0) {
2878              $arr =& $this->GetArray($nrows);
2879              return $arr;
2880          } 
2881          
2882          $this->Move($offset);
2883          
2884          $results = array();
2885          $cnt = 0;
2886          while (!$this->EOF && $nrows != $cnt) {
2887              $results[$cnt++] = $this->fields;
2888              $this->MoveNext();
2889          }
2890          
2891          return $results;
2892      }
2893      
2894      
2895      /**
2896       * Synonym for GetArray() for compatibility with ADO.
2897       *
2898       * @param [nRows]  is the number of rows to return. -1 means every row.
2899       *
2900       * @return an array indexed by the rows (0-based) from the recordset
2901       */
2902      function &GetRows($nRows = -1) 
2903      {
2904          $arr =& $this->GetArray($nRows);
2905          return $arr;
2906      }
2907      
2908      /**
2909       * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. 
2910       * The first column is treated as the key and is not included in the array. 
2911       * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
2912       * $force_array == true.
2913       *
2914       * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
2915       *     array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
2916       *     read the source.
2917       *
2918       * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and 
2919       * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
2920       *
2921       * @return an associative array indexed by the first column of the array, 
2922       *     or false if the  data has less than 2 cols.
2923       */
2924      function &GetAssoc($force_array = false, $first2cols = false) 
2925      {
2926      global $ADODB_EXTENSION;
2927      
2928          $cols = $this->_numOfFields;
2929          if ($cols < 2) {
2930              $false = false;
2931              return $false;
2932          }
2933          $numIndex = isset($this->fields[0]);
2934          $results = array();
2935          
2936          if (!$first2cols && ($cols > 2 || $force_array)) {
2937              if ($ADODB_EXTENSION) {
2938                  if ($numIndex) {
2939                      while (!$this->EOF) {
2940                          // $results[trim($this->fields[0])] = array_slice($this->fields, 1);
2941                          // Fix for array_slice re-numbering numeric associative keys in PHP5
2942                          $keys = array_slice(array_keys($this->fields), 1);
2943                          $sliced_array = array();
2944  
2945                          foreach($keys as $key) {
2946                              $sliced_array[$key] = $this->fields[$key];
2947                          }
2948                          
2949                          $results[trim(reset($this->fields))] = $sliced_array;
2950  
2951                          adodb_movenext($this);
2952                      }
2953                  } else {
2954                      while (!$this->EOF) {
2955                          $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2956                          adodb_movenext($this);
2957                      }
2958                  }
2959              } else {
2960                  if ($numIndex) {
2961                      while (!$this->EOF) {
2962                          //$results[trim($this->fields[0])] = array_slice($this->fields, 1);
2963                          // Fix for array_slice re-numbering numeric associative keys in PHP5
2964                          $keys = array_slice(array_keys($this->fields), 1);
2965                          $sliced_array = array();
2966  
2967                          foreach($keys as $key) {
2968                              $sliced_array[$key] = $this->fields[$key];
2969                          }
2970                          
2971                          $results[trim(reset($this->fields))] = $sliced_array;
2972                          $this->MoveNext();
2973                      }
2974                  } else {
2975                      while (!$this->EOF) {
2976                          $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2977                          $this->MoveNext();
2978                      }
2979                  }
2980              }
2981          } else {
2982              if ($ADODB_EXTENSION) {
2983                  // return scalar values
2984                  if ($numIndex) {
2985                      while (!$this->EOF) {
2986                      // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2987                          $results[trim(($this->fields[0]))] = $this->fields[1];
2988                          adodb_movenext($this);
2989                      }
2990                  } else {
2991                      while (!$this->EOF) {
2992                      // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2993                          $v1 = trim(reset($this->fields));
2994                          $v2 = ''.next($this->fields); 
2995                          $results[$v1] = $v2;
2996                          adodb_movenext($this);
2997                      }
2998                  }
2999              } else {
3000                  if ($numIndex) {
3001                      while (!$this->EOF) {
3002                      // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3003                          $results[trim(($this->fields[0]))] = $this->fields[1];
3004                          $this->MoveNext();
3005                      }
3006                  } else {
3007                      while (!$this->EOF) {
3008                      // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3009                          $v1 = trim(reset($this->fields));
3010                          $v2 = ''.next($this->fields); 
3011                          $results[$v1] = $v2;
3012                          $this->MoveNext();
3013                      }
3014                  }
3015              }
3016          }
3017          
3018          $ref =& $results; # workaround accelerator incompat with PHP 4.4 :(
3019          return $ref; 
3020      }
3021      
3022      
3023      /**
3024       *
3025       * @param v      is the character timestamp in YYYY-MM-DD hh:mm:ss format
3026       * @param fmt     is the format to apply to it, using date()
3027       *
3028       * @return a timestamp formated as user desires
3029       */
3030  	function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
3031      {
3032          if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v);
3033          $tt = $this->UnixTimeStamp($v);
3034          // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3035          if (($tt === false || $tt == -1) && $v != false) return $v;
3036          if ($tt === 0) return $this->emptyTimeStamp;
3037          return adodb_date($fmt,$tt);
3038      }
3039      
3040      
3041      /**
3042       * @param v      is the character date in YYYY-MM-DD format, returned by database
3043       * @param fmt     is the format to apply to it, using date()
3044       *
3045       * @return a date formated as user desires
3046       */
3047  	function UserDate($v,$fmt='Y-m-d')
3048      {
3049          $tt = $this->UnixDate($v);
3050          // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3051          if (($tt === false || $tt == -1) && $v != false) return $v;
3052          else if ($tt == 0) return $this->emptyDate;
3053          else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
3054          }
3055          return adodb_date($fmt,$tt);
3056      }
3057      
3058      
3059      /**
3060       * @param $v is a date string in YYYY-MM-DD format
3061       *
3062       * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3063       */
3064  	function UnixDate($v)
3065      {
3066          return ADOConnection::UnixDate($v);
3067      }
3068      
3069  
3070      /**
3071       * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
3072       *
3073       * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3074       */
3075  	function UnixTimeStamp($v)
3076      {
3077          return ADOConnection::UnixTimeStamp($v);
3078      }
3079      
3080      
3081      /**
3082      * PEAR DB Compat - do not use internally
3083      */
3084  	function Free()
3085      {
3086          return $this->Close();
3087      }
3088      
3089      
3090      /**
3091      * PEAR DB compat, number of rows
3092      */
3093  	function NumRows()
3094      {
3095          return $this->_numOfRows;
3096      }
3097      
3098      
3099      /**
3100      * PEAR DB compat, number of cols
3101      */
3102  	function NumCols()
3103      {
3104          return $this->_numOfFields;
3105      }
3106      
3107      /**
3108      * Fetch a row, returning false if no more rows. 
3109      * This is PEAR DB compat mode.
3110      *
3111      * @return false or array containing the current record
3112      */
3113      function &FetchRow()
3114      {
3115          if ($this->EOF) {
3116              $false = false;
3117              return $false;
3118          }
3119          $arr = $this->fields;
3120          $this->_currentRow++;
3121          if (!$this->_fetch()) $this->EOF = true;
3122          return $arr;
3123      }
3124      
3125      
3126      /**
3127      * Fetch a row, returning PEAR_Error if no more rows. 
3128      * This is PEAR DB compat mode.
3129      *
3130      * @return DB_OK or error object
3131      */
3132  	function FetchInto(&$arr)
3133      {
3134          if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
3135          $arr = $this->fields;
3136          $this->MoveNext();
3137          return 1; // DB_OK
3138      }
3139      
3140      
3141      /**
3142       * Move to the first row in the recordset. Many databases do NOT support this.
3143       *
3144       * @return true or false
3145       */
3146  	function MoveFirst() 
3147      {
3148          if ($this->_currentRow == 0) return true;
3149          return $this->Move(0);            
3150      }            
3151  
3152      
3153      /**
3154       * Move to the last row in the recordset. 
3155       *
3156       * @return true or false
3157       */
3158  	function MoveLast() 
3159      {
3160          if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
3161          if ($this->EOF) return false;
3162          while (!$this->EOF) {
3163              $f = $this->fields;
3164              $this->MoveNext();
3165          }
3166          $this->fields = $f;
3167          $this->EOF = false;
3168          return true;
3169      }
3170      
3171      
3172      /**
3173       * Move to next record in the recordset.
3174       *
3175       * @return true if there still rows available, or false if there are no more rows (EOF).
3176       */
3177  	function MoveNext() 
3178      {
3179          if (!$this->EOF) {
3180              $this->_currentRow++;
3181              if ($this->_fetch()) return true;
3182          }
3183          $this->EOF = true;
3184          /* -- tested error handling when scrolling cursor -- seems useless.
3185          $conn = $this->connection;
3186          if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
3187              $fn = $conn->raiseErrorFn;
3188              $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
3189          }
3190          */
3191          return false;
3192      }
3193      
3194      
3195      /**
3196       * Random access to a specific row in the recordset. Some databases do not support
3197       * access to previous rows in the databases (no scrolling backwards).
3198       *
3199       * @param rowNumber is the row to move to (0-based)
3200       *
3201       * @return true if there still rows available, or false if there are no more rows (EOF).
3202       */
3203  	function Move($rowNumber = 0) 
3204      {
3205          $this->EOF = false;
3206          if ($rowNumber == $this->_currentRow) return true;
3207          if ($rowNumber >= $this->_numOfRows)
3208                 if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
3209                    
3210          if ($this->canSeek) { 
3211      
3212              if ($this->_seek($rowNumber)) {
3213                  $this->_currentRow = $rowNumber;
3214                  if ($this->_fetch()) {
3215                      return true;
3216                  }
3217              } else {
3218                  $this->EOF = true;
3219                  return false;
3220              }
3221          } else {
3222              if ($rowNumber < $this->_currentRow) return false;
3223              global $ADODB_EXTENSION;
3224              if ($ADODB_EXTENSION) {
3225                  while (!$this->EOF && $this->_currentRow < $rowNumber) {
3226                      adodb_movenext($this);
3227                  }
3228              } else {
3229              
3230                  while (! $this->EOF && $this->_currentRow < $rowNumber) {
3231                      $this->_currentRow++;
3232                      
3233                      if (!$this->_fetch()) $this->EOF = true;
3234                  }
3235              }
3236              return !($this->EOF);
3237          }
3238          
3239          $this->fields = false;    
3240          $this->EOF = true;
3241          return false;
3242      }
3243      
3244          
3245      /**
3246       * Get the value of a field in the current row by column name.
3247       * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
3248       * 
3249       * @param colname  is the field to access
3250       *
3251       * @return the value of $colname column
3252       */
3253  	function Fields($colname)
3254      {
3255          return $this->fields[$colname];
3256      }
3257      
3258  	function GetAssocKeys($upper=true)
3259      {
3260          $this->bind = array();
3261          for ($i=0; $i < $this->_numOfFields; $i++) {
3262              $o = $this->FetchField($i);
3263              if ($upper === 2) $this->bind[$o->name] = $i;
3264              else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
3265          }
3266      }
3267      
3268    /**
3269     * Use associative array to get fields array for databases that do not support
3270     * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
3271     *
3272     * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
3273     * before you execute your SQL statement, and access $rs->fields['col'] directly.
3274     *
3275     * $upper  0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
3276     */
3277      function &GetRowAssoc($upper=1)
3278      {
3279          $record = array();
3280       //    if (!$this->fields) return $record;
3281          
3282             if (!$this->bind) {
3283              $this->GetAssocKeys($upper);
3284          }
3285          
3286          foreach($this->bind as $k => $v) {
3287              $record[$k] = $this->fields[$v];
3288          }
3289  
3290          return $record;
3291      }
3292      
3293      
3294      /**
3295       * Clean up recordset
3296       *
3297       * @return true or false
3298       */
3299  	function Close() 
3300      {
3301          // free connection object - this seems to globally free the object
3302          // and not merely the reference, so don't do this...
3303          // $this->connection = false; 
3304          if (!$this->_closed) {
3305              $this->_closed = true;
3306              return $this->_close();        
3307          } else
3308              return true;
3309      }
3310      
3311      /**
3312       * synonyms RecordCount and RowCount    
3313       *
3314       * @return the number of rows or -1 if this is not supported
3315       */
3316  	function RecordCount() {return $this->_numOfRows;}
3317      
3318      
3319      /*
3320      * If we are using PageExecute(), this will return the maximum possible rows
3321      * that can be returned when paging a recordset.
3322      */
3323  	function MaxRecordCount()
3324      {
3325          return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
3326      }
3327      
3328      /**
3329       * synonyms RecordCount and RowCount    
3330       *
3331       * @return the number of rows or -1 if this is not supported
3332       */
3333  	function RowCount() {return $this->_numOfRows;} 
3334      
3335  
3336       /**
3337       * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
3338       *
3339       * @return  the number of records from a previous SELECT. All databases support this.
3340       *
3341       * But aware possible problems in multiuser environments. For better speed the table
3342       * must be indexed by the condition. Heavy test this before deploying.
3343       */ 
3344  	function PO_RecordCount($table="", $condition="") {
3345          
3346          $lnumrows = $this->_numOfRows;
3347          // the database doesn't support native recordcount, so we do a workaround
3348          if ($lnumrows == -1 && $this->connection) {
3349              IF ($table) {
3350                  if ($condition) $condition = " WHERE " . $condition; 
3351                  $resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
3352                  if ($resultrows) $lnumrows = reset($resultrows->fields);
3353              }
3354          }
3355          return $lnumrows;
3356      }
3357      
3358      
3359      /**
3360       * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3361       */
3362  	function CurrentRow() {return $this->_currentRow;}
3363      
3364      /**
3365       * synonym for CurrentRow -- for ADO compat
3366       *
3367       * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3368       */
3369  	function AbsolutePosition() {return $this->_currentRow;}
3370      
3371      /**
3372       * @return the number of columns in the recordset. Some databases will set this to 0
3373       * if no records are returned, others will return the number of columns in the query.
3374       */
3375  	function FieldCount() {return $this->_numOfFields;}   
3376  
3377  
3378      /**
3379       * Get the ADOFieldObject of a specific column.
3380       *
3381       * @param fieldoffset    is the column position to access(0-based).
3382       *
3383       * @return the ADOFieldObject for that column, or false.
3384       */
3385      function &FetchField($fieldoffset) 
3386      {
3387          // must be defined by child class
3388      }    
3389      
3390      /**
3391       * Get the ADOFieldObjects of all columns in an array.
3392       *
3393       */
3394      function& FieldTypesArray()
3395      {
3396          $arr = array();
3397          for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) 
3398              $arr[] = $this->FetchField($i);
3399          return $arr;
3400      }
3401      
3402      /**
3403      * Return the fields array of the current row as an object for convenience.
3404      * The default case is lowercase field names.
3405      *
3406      * @return the object with the properties set to the fields of the current row
3407      */
3408      function &FetchObj()
3409      {
3410          $o =& $this->FetchObject(false);
3411          return $o;
3412      }
3413      
3414      /**
3415      * Return the fields array of the current row as an object for convenience.
3416      * The default case is uppercase.
3417      * 
3418      * @param $isupper to set the object property names to uppercase
3419      *
3420      * @return the object with the properties set to the fields of the current row
3421      */
3422      function &FetchObject($isupper=true)
3423      {
3424          if (empty($this->_obj)) {
3425              $this->_obj = new ADOFetchObj();
3426              $this->_names = array();
3427              for ($i=0; $i <$this->_numOfFields; $i++) {
3428                  $f = $this->FetchField($i);
3429                  $this->_names[] = $f->name;
3430              }
3431          }
3432          $i = 0;
3433          if (PHP_VERSION >= 5) $o = clone($this->_obj);
3434          else $o = $this->_obj;
3435      
3436          for ($i=0; $i <$this->_numOfFields; $i++) {
3437              $name = $this->_names[$i];
3438              if ($isupper) $n = strtoupper($name);
3439              else $n = $name;
3440              
3441              $o->$n = $this->Fields($name);
3442          }
3443          return $o;
3444      }
3445      
3446      /**
3447      * Return the fields array of the current row as an object for convenience.
3448      * The default is lower-case field names.
3449      * 
3450      * @return the object with the properties set to the fields of the current row,
3451      *     or false if EOF
3452      *
3453      * Fixed bug reported by tim@orotech.net
3454      */
3455      function &FetchNextObj()
3456      {
3457          $o =& $this->FetchNextObject(false);
3458          return $o;
3459      }
3460      
3461      
3462      /**
3463      * Return the fields array of the current row as an object for convenience. 
3464      * The default is upper case field names.
3465      * 
3466      * @param $isupper to set the object property names to uppercase
3467      *
3468      * @return the object with the properties set to the fields of the current row,
3469      *     or false if EOF
3470      *
3471      * Fixed bug reported by tim@orotech.net
3472      */
3473      function &FetchNextObject($isupper=true)
3474      {
3475          $o = false;
3476          if ($this->_numOfRows != 0 && !$this->EOF) {
3477              $o = $this->FetchObject($isupper);    
3478              $this->_currentRow++;
3479              if ($this->_fetch()) return $o;
3480          }
3481          $this->EOF = true;
3482          return $o;
3483      }
3484      
3485      /**
3486       * Get the metatype of the column. This is used for formatting. This is because
3487       * many databases use different names for the same type, so we transform the original
3488       * type to our standardised version which uses 1 character codes:
3489       *
3490       * @param t  is the type passed in. Normally is ADOFieldObject->type.
3491       * @param len is the maximum length of that field. This is because we treat character
3492       *     fields bigger than a certain size as a 'B' (blob).
3493       * @param fieldobj is the field object returned by the database driver. Can hold
3494       *    additional info (eg. primary_key for mysql).
3495       * 
3496       * @return the general type of the data: 
3497       *    C for character < 250 chars
3498       *    X for teXt (>= 250 chars)
3499       *    B for Binary
3500       *     N for numeric or floating point
3501       *    D for date
3502       *    T for timestamp
3503       *     L for logical/Boolean
3504       *    I for integer
3505       *    R for autoincrement counter/integer
3506       * 
3507       *
3508      */
3509  	function MetaType($t,$len=-1,$fieldobj=false)
3510      {
3511          if (is_object($t)) {
3512              $fieldobj = $t;
3513              $t = $fieldobj->type;
3514              $len = $fieldobj->max_length;
3515          }
3516      // changed in 2.32 to hashing instead of switch stmt for speed...
3517      static $typeMap = array(
3518          'VARCHAR' => 'C',
3519          'VARCHAR2' => 'C',
3520          'CHAR' => 'C',
3521          'C' => 'C',
3522          'STRING' => 'C',
3523          'NCHAR' => 'C',
3524          'NVARCHAR' => 'C',
3525          'VARYING' => 'C',
3526          'BPCHAR' => 'C',
3527          'CHARACTER' => 'C',
3528          'INTERVAL' => 'C',  # Postgres
3529          'MACADDR' => 'C', # postgres
3530          ##
3531          'LONGCHAR' => 'X',
3532          'TEXT' => 'X',
3533          'NTEXT' => 'X',
3534          'M' => 'X',
3535          'X' => 'X',
3536          'CLOB' => 'X',
3537          'NCLOB' => 'X',
3538          'LVARCHAR' => 'X',
3539          ##
3540          'BLOB' => 'B',
3541          'IMAGE' => 'B',
3542          'BINARY' => 'B',
3543          'VARBINARY' => 'B',
3544          'LONGBINARY' => 'B',
3545          'B' => 'B',
3546          ##
3547          'YEAR' => 'D', // mysql
3548          'DATE' => 'D',
3549          'D' => 'D',
3550          ##
3551          'TIME' => 'T',
3552          'TIMESTAMP' => 'T',
3553          'DATETIME' => 'T',
3554          'TIMESTAMPTZ' => 'T',
3555          'T' => 'T',
3556          'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
3557          ##
3558          'BOOL' => 'L',
3559          'BOOLEAN' => 'L', 
3560          'BIT' => 'L',
3561          'L' => 'L',
3562          ##
3563          'COUNTER' => 'R',
3564          'R' => 'R',
3565          'SERIAL' => 'R', // ifx
3566          'INT IDENTITY' => 'R',
3567          ##
3568          'INT' => 'I',
3569          'INT2' => 'I',
3570          'INT4' => 'I',
3571          'INT8' => 'I',
3572          'INTEGER' => 'I',
3573          'INTEGER UNSIGNED' => 'I',
3574          'SHORT' => 'I',
3575          'TINYINT' => 'I',
3576          'SMALLINT' => 'I',
3577          'I' => 'I',
3578          ##
3579          'LONG' => 'N', // interbase is numeric, oci8 is blob
3580          'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
3581          'DECIMAL' => 'N',
3582          'DEC' => 'N',
3583          'REAL' => 'N',
3584          'DOUBLE' => 'N',
3585          'DOUBLE PRECISION' => 'N',
3586          'SMALLFLOAT' => 'N',
3587          'FLOAT' => 'N',
3588          'NUMBER' => 'N',
3589          'NUM' => 'N',
3590          'NUMERIC' => 'N',
3591          'MONEY' => 'N',
3592          
3593          ## informix 9.2
3594          'SQLINT' => 'I', 
3595          'SQLSERIAL' => 'I', 
3596          'SQLSMINT' => 'I', 
3597          'SQLSMFLOAT' => 'N', 
3598          'SQLFLOAT' => 'N', 
3599          'SQLMONEY' => 'N', 
3600          'SQLDECIMAL' => 'N', 
3601          'SQLDATE' => 'D', 
3602          'SQLVCHAR' => 'C', 
3603          'SQLCHAR' => 'C', 
3604          'SQLDTIME' => 'T', 
3605          'SQLINTERVAL' => 'N', 
3606          'SQLBYTES' => 'B', 
3607          'SQLTEXT' => 'X',
3608           ## informix 10
3609          "SQLINT8" => 'I8',
3610          "SQLSERIAL8" => 'I8',
3611          "SQLNCHAR" => 'C',
3612          "SQLNVCHAR" => 'C',
3613          "SQLLVARCHAR" => 'X',
3614          "SQLBOOL" => 'L'
3615          );
3616          
3617          $tmap = false;
3618          $t = strtoupper($t);
3619          $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N';
3620          switch ($tmap) {
3621          case 'C':
3622          
3623              // is the char field is too long, return as text field... 
3624              if ($this->blobSize >= 0) {
3625                  if ($len > $this->blobSize) return 'X';
3626              } else if ($len > 250) {
3627                  return 'X';
3628              }
3629              return 'C';
3630              
3631          case 'I':
3632              if (!empty($fieldobj->primary_key)) return 'R';
3633              return 'I';
3634          
3635          case false:
3636              return 'N';
3637              
3638          case 'B':
3639               if (isset($fieldobj->binary)) 
3640                   return ($fieldobj->binary) ? 'B' : 'X';
3641              return 'B';
3642          
3643          case 'D':
3644              if (!empty($this->connection) && !empty($this->connection->datetime)) return 'T';
3645              return 'D';
3646              
3647          default: 
3648              if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B';
3649              return $tmap;
3650          }
3651      }
3652      
3653      
3654  	function _close() {}
3655      
3656      /**
3657       * set/returns the current recordset page when paginating
3658       */
3659  	function AbsolutePage($page=-1)
3660      {
3661          if ($page != -1) $this->_currentPage = $page;
3662          return $this->_currentPage;
3663      }
3664      
3665      /**
3666       * set/returns the status of the atFirstPage flag when paginating
3667       */
3668  	function AtFirstPage($status=false)
3669      {
3670          if ($status != false) $this->_atFirstPage = $status;
3671          return $this->_atFirstPage;
3672      }
3673      
3674  	function LastPageNo($page = false)
3675      {
3676          if ($page != false) $this->_lastPageNo = $page;
3677          return $this->_lastPageNo;
3678      }
3679      
3680      /**
3681       * set/returns the status of the atLastPage flag when paginating
3682       */
3683  	function AtLastPage($status=false)
3684      {
3685          if ($status != false) $this->_atLastPage = $status;
3686          return $this->_atLastPage;
3687      }
3688      
3689  } // end class ADORecordSet
3690      
3691      //==============================================================================================    
3692      // CLASS ADORecordSet_array
3693      //==============================================================================================    
3694      
3695      /**
3696       * This class encapsulates the concept of a recordset created in memory
3697       * as an array. This is useful for the creation of cached recordsets.
3698       * 
3699       * Note that the constructor is different from the standard ADORecordSet
3700       */
3701      
3702      class ADORecordSet_array extends ADORecordSet
3703      {
3704          var $databaseType = 'array';
3705  
3706          var $_array;     // holds the 2-dimensional data array
3707          var $_types;    // the array of types of each column (C B I L M)
3708          var $_colnames;    // names of each column in array
3709          var $_skiprow1;    // skip 1st row because it holds column names
3710          var $_fieldobjects; // holds array of field objects
3711          var $canSeek = true;
3712          var $affectedrows = false;
3713          var $insertid = false;
3714          var $sql = '';
3715          var $compat = false;
3716          /**
3717           * Constructor
3718           *
3719           */
3720  		function ADORecordSet_array($fakeid=1)
3721          {
3722          global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
3723          
3724              // fetch() on EOF does not delete $this->fields
3725              $this->compat = !empty($ADODB_COMPAT_FETCH);
3726              $this->ADORecordSet($fakeid); // fake queryID        
3727              $this->fetchMode = $ADODB_FETCH_MODE;
3728          }
3729          
3730  		function _transpose()
3731          {
3732          global $ADODB_INCLUDED_LIB;
3733              
3734              if (empty($ADODB_INCLUDED_LIB)) include (ADODB_DIR.'/adodb-lib.inc.php');
3735              $hdr = true;
3736              
3737              adodb_transpose($this->_array, $newarr, $hdr);
3738              //adodb_pr($newarr);
3739              
3740              $this->_skiprow1 = false;
3741              $this->_array =& $newarr;
3742              $this->_colnames = $hdr;
3743              
3744              adodb_probetypes($newarr,$this->_types);
3745          
3746              $this->_fieldobjects = array();
3747              
3748              foreach($hdr as $k => $name) {
3749                  $f = new ADOFieldObject();
3750                  $f->name = $name;
3751                  $f->type = $this->_types[$k];
3752                  $f->max_length = -1;
3753                  $this->_fieldobjects[] = $f;
3754                  
3755              }
3756              $this->fields = reset($this->_array);
3757              
3758              $this->_initrs();
3759              
3760          }
3761          
3762          /**
3763           * Setup the array.
3764           *
3765           * @param array        is a 2-dimensional array holding the data.
3766           *            The first row should hold the column names 
3767           *            unless paramter $colnames is used.
3768           * @param typearr    holds an array of types. These are the same types 
3769           *            used in MetaTypes (C,B,L,I,N).
3770           * @param [colnames]    array of column names. If set, then the first row of
3771           *            $array should not hold the column names.
3772           */
3773  		function InitArray($array,$typearr,$colnames=false)
3774          {
3775              $this->_array = $array;
3776              $this->_types = $typearr;    
3777              if ($colnames) {
3778                  $this->_skiprow1 = false;
3779                  $this->_colnames = $colnames;
3780              } else  {
3781                  $this->_skiprow1 = true;
3782                  $this->_colnames = $array[0];
3783              }
3784              $this->Init();
3785          }
3786          /**
3787           * Setup the Array and datatype file objects
3788           *
3789           * @param array        is a 2-dimensional array holding the data.
3790           *            The first row should hold the column names 
3791           *            unless paramter $colnames is used.
3792           * @param fieldarr    holds an array of ADOFieldObject's.
3793           */
3794  		function InitArrayFields(&$array,&$fieldarr)
3795          {
3796              $this->_array =& $array;
3797              $this->_skiprow1= false;
3798              if ($fieldarr) {
3799                  $this->_fieldobjects =& $fieldarr;
3800              } 
3801              $this->Init();
3802          }
3803          
3804          function &GetArray($nRows=-1)
3805          {
3806              if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
3807                  return $this->_array;
3808              } else {
3809                  $arr =& ADORecordSet::GetArray($nRows);
3810                  return $arr;
3811              }
3812          }
3813          
3814  		function _initrs()
3815          {
3816              $this->_numOfRows =  sizeof($this->_array);
3817              if ($this->_skiprow1) $this->_numOfRows -= 1;
3818          
3819              $this->_numOfFields =(isset($this->_fieldobjects)) ?
3820                   sizeof($this->_fieldobjects):sizeof($this->_types);
3821          }
3822          
3823          /* Use associative array to get fields array */
3824  		function Fields($colname)
3825          {
3826              $mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
3827              
3828              if ($mode & ADODB_FETCH_ASSOC) {
3829                  if (!isset($this->fields[$colname])) $colname = strtolower($colname);
3830                  return $this->fields[$colname];
3831              }
3832              if (!$this->bind) {
3833                  $this->bind = array();
3834                  for ($i=0; $i < $this->_numOfFields; $i++) {
3835                      $o = $this->FetchField($i);
3836                      $this->bind[strtoupper($o->name)] = $i;
3837                  }
3838              }
3839              return $this->fields[$this->bind[strtoupper($colname)]];
3840          }
3841          
3842          function &FetchField($fieldOffset = -1) 
3843          {
3844              if (isset($this->_fieldobjects)) {
3845                  return $this->_fieldobjects[$fieldOffset];
3846              }
3847              $o =  new ADOFieldObject();
3848              $o->name = $this->_colnames[$fieldOffset];
3849              $o->type =  $this->_types[$fieldOffset];
3850              $o->max_length = -1; // length not known
3851              
3852              return $o;
3853          }
3854              
3855  		function _seek($row)
3856          {
3857              if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
3858                  $this->_currentRow = $row;
3859                  if ($this->_skiprow1) $row += 1;
3860                  $this->fields = $this->_array[$row];
3861                  return true;
3862              }
3863              return false;
3864          }
3865          
3866  		function MoveNext() 
3867          {
3868              if (!$this->EOF) {        
3869                  $this->_currentRow++;
3870                  
3871                  $pos = $this->_currentRow;
3872                  
3873                  if ($this->_numOfRows <= $pos) {
3874                      if (!$this->compat) $this->fields = false;
3875                  } else {
3876                      if ($this->_skiprow1) $pos += 1;
3877                      $this->fields = $this->_array[$pos];
3878                      return true;
3879                  }        
3880                  $this->EOF = true;
3881              }
3882              
3883              return false;
3884          }    
3885      
3886  		function _fetch()
3887          {
3888              $pos = $this->_currentRow;
3889              
3890              if ($this->_numOfRows <= $pos) {
3891                  if (!$this->compat) $this->fields = false;
3892                  return false;
3893              }
3894              if ($this->_skiprow1) $pos += 1;
3895              $this->fields = $this->_array[$pos];
3896              return true;
3897          }
3898          
3899  		function _close() 
3900          {
3901              return true;    
3902          }
3903      
3904      } // ADORecordSet_array
3905  
3906      //==============================================================================================    
3907      // HELPER FUNCTIONS
3908      //==============================================================================================            
3909      
3910      /**
3911       * Synonym for ADOLoadCode. Private function. Do not use.
3912       *
3913       * @deprecated
3914       */
3915  	function ADOLoadDB($dbType) 
3916      { 
3917          return ADOLoadCode($dbType);
3918      }
3919          
3920      /**
3921       * Load the code for a specific database driver. Private function. Do not use.
3922       */
3923  	function ADOLoadCode($dbType) 
3924      {
3925      global $ADODB_LASTDB;
3926      
3927          if (!$dbType) return false;
3928          $db = strtolower($dbType);
3929          switch ($db) {
3930              case 'ado': 
3931                  if (PHP_VERSION >= 5) $db = 'ado5';
3932                  $class = 'ado'; 
3933                  break;
3934              case 'ifx':
3935              case 'maxsql': $class = $db = 'mysqlt'; break;
3936              case 'postgres':
3937              case 'postgres8':
3938              case 'pgsql': $class = $db = 'postgres7'; break;
3939              default:
3940                  $class = $db; break;
3941          }
3942          
3943          $file = ADODB_DIR."/drivers/adodb-".$db.".inc.php";
3944          @include_once($file);
3945          $ADODB_LASTDB = $class;
3946          if (class_exists("ADODB_" . $class)) return $class;
3947          
3948          //ADOConnection::outp(adodb_pr(get_declared_classes(),true));
3949          if (!file_exists($file)) ADOConnection::outp("Missing file: $file");
3950          else ADOConnection::outp("Syntax error in file: $file");
3951          return false;
3952      }
3953  
3954      /**
3955       * synonym for ADONewConnection for people like me who cannot remember the correct name
3956       */
3957      function &NewADOConnection($db='')
3958      {
3959          $tmp =& ADONewConnection($db);
3960          return $tmp;
3961      }
3962      
3963      /**
3964       * Instantiate a new Connection class for a specific database driver.
3965       *
3966       * @param [db]  is the database Connection object to create. If undefined,
3967       *     use the last database driver that was loaded by ADOLoadCode().
3968       *
3969       * @return the freshly created instance of the Connection class.
3970       */
3971      function &ADONewConnection($db='')
3972      {
3973      GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB;
3974          
3975          if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
3976          $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
3977          $false = false;
3978          if ($at = strpos($db,'://')) {
3979              $origdsn = $db;
3980              if (PHP_VERSION < 5) $dsna = @parse_url($db);
3981              else {
3982                  $fakedsn = 'fake'.substr($db,$at);
3983                  $dsna = @parse_url($fakedsn);
3984                  $dsna['scheme'] = substr($db,0,$at);
3985              
3986                  if (strncmp($db,'pdo',3) == 0) {
3987                      $sch = explode('_',$dsna['scheme']);
3988                      if (sizeof($sch)>1) {
3989                          $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
3990                          $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
3991                          $dsna['scheme'] = 'pdo';
3992                      }
3993                  }
3994              }
3995              
3996              if (!$dsna) {
3997                  // special handling of oracle, which might not have host
3998                  $db = str_replace('@/','@adodb-fakehost/',$db);
3999                  $dsna = parse_url($db);
4000                  if (!$dsna) return $false;
4001                  $dsna['host'] = '';
4002              }
4003              $db = @$dsna['scheme'];
4004              if (!$db) return $false;
4005              $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
4006              $dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
4007              $dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
4008              $dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
4009              
4010              if (isset($dsna['query'])) {
4011                  $opt1 = explode('&',$dsna['query']);
4012                  foreach($opt1 as $k => $v) {
4013                      $arr = explode('=',$v);
4014                      $opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
4015                  }
4016              } else $opt = array();
4017          }
4018      /*
4019       *  phptype: Database backend used in PHP (mysql, odbc etc.)
4020       *  dbsyntax: Database used with regards to SQL syntax etc.
4021       *  protocol: Communication protocol to use (tcp, unix etc.)
4022       *  hostspec: Host specification (hostname[:port])
4023       *  database: Database to use on the DBMS server
4024       *  username: User name for login
4025       *  password: Password for login
4026       */
4027          if (!empty($ADODB_NEWCONNECTION)) {
4028              $obj = $ADODB_NEWCONNECTION($db);
4029  
4030          } else {
4031          
4032              if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
4033              if (empty($db)) $db = $ADODB_LASTDB;
4034              
4035              if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db);
4036              
4037              if (!$db) {
4038                  if (isset($origdsn)) $db = $origdsn;
4039                  if ($errorfn) {
4040                      // raise an error
4041                      $ignore = false;
4042                      $errorfn('ADONewConnection', 'ADONewConnection', -998,
4043                               "could not load the database driver for '$db'",
4044                               $db,false,$ignore);
4045                  } else
4046                       ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
4047                      
4048                  return $false;
4049              }
4050              
4051              $cls = 'ADODB_'.$db;
4052              if (!class_exists($cls)) {
4053                  adodb_backtrace();
4054                  return $false;
4055              }
4056              
4057              $obj = new $cls();
4058          }
4059          
4060          # constructor should not fail
4061          if ($obj) {
4062              if ($errorfn)  $obj->raiseErrorFn = $errorfn;
4063              if (isset($dsna)) {
4064                  if (isset($dsna['port'])) $obj->port = $dsna['port'];
4065                  foreach($opt as $k => $v) {
4066                      switch(strtolower($k)) {
4067                      case 'new':
4068                                          $nconnect = true; $persist = true; break;
4069                      case 'persist':
4070                      case 'persistent':     $persist = $v; break;
4071                      case 'debug':        $obj->debug = (integer) $v; break;
4072                      #ibase
4073                      case 'role':        $obj->role = $v; break;
4074                      case 'dialect':     $obj->dialect = (integer) $v; break;
4075                      case 'charset':        $obj->charset = $v; $obj->charSet=$v; break;
4076                      case 'buffers':        $obj->buffers = $v; break;
4077                      case 'fetchmode':   $obj->SetFetchMode($v); break;
4078                      #ado
4079                      case 'charpage':    $obj->charPage = $v; break;
4080                      #mysql, mysqli
4081                      case 'clientflags': $obj->clientFlags = $v; break;
4082                      #mysql, mysqli, postgres
4083                      case 'port': $obj->port = $v; break;
4084                      #mysqli
4085                      case 'socket': $obj->socket = $v; break;
4086                      #oci8
4087                      case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
4088                      }
4089                  }
4090                  if (empty($persist))
4091                      $ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4092                  else if (empty($nconnect))
4093                      $ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4094                  else
4095                      $ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4096                      
4097                  if (!$ok) return $false;
4098              }
4099          }
4100          return $obj;
4101      }
4102      
4103      
4104      
4105      // $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
4106  	function _adodb_getdriver(