Search moodle.org's
Developer Documentation

See Release Notes

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

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