Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

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