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