Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

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