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