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