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