Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

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