Syntax Desktop PHP Cross Reference Web Portal Systems

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