[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

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


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1