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