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