[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/adodb/ -> adodb.inc.php (source)

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


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7