Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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