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