1<?php 2/* 3 * Set tabs to 4 for best viewing. 4 * 5 * Latest version is available at http://adodb.sourceforge.net 6 * 7 * This is the main include file for ADOdb. 8 * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php 9 * 10 * The ADOdb files are formatted so that doxygen can be used to generate documentation. 11 * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/ 12 */ 13 14/** 15 \mainpage 16 17 @version V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim#natsoft.com.my). All rights reserved. 18 19 Released under both BSD license and Lesser GPL library license. You can choose which license 20 you prefer. 21 22 PHP's database access functions are not standardised. This creates a need for a database 23 class library to hide the differences between the different database API's (encapsulate 24 the differences) so we can easily switch databases. 25 26 We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2, 27 Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access, 28 ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and 29 other databases via ODBC. 30 31 Latest Download at http://php.weblogs.com/adodb<br> 32 Manual is at http://php.weblogs.com/adodb_manual 33 34 */ 35 36 if (!defined('_ADODB_LAYER')) { 37 define('_ADODB_LAYER',1); 38 39 //============================================================================================== 40 // CONSTANT DEFINITIONS 41 //============================================================================================== 42 43 44 /** 45 * Set ADODB_DIR to the directory where this file resides... 46 * This constant was formerly called $ADODB_RootPath 47 */ 48 if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__)); 49 50 //============================================================================================== 51 // GLOBAL VARIABLES 52 //============================================================================================== 53 54 GLOBAL 55 $ADODB_vers, // database version 56 $ADODB_COUNTRECS, // count number of records returned - slows down query 57 $ADODB_CACHE_DIR, // directory to cache recordsets 58 $ADODB_EXTENSION, // ADODB extension installed 59 $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF 60 $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default... 61 62 //============================================================================================== 63 // GLOBAL SETUP 64 //============================================================================================== 65 66 $ADODB_EXTENSION = defined('ADODB_EXTENSION'); 67 68 //********************************************************// 69 /* 70 Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3). 71 Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi 72 73 0 = ignore empty fields. All empty fields in array are ignored. 74 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values. 75 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values. 76 3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values. 77 */ 78 define('ADODB_FORCE_IGNORE',0); 79 define('ADODB_FORCE_NULL',1); 80 define('ADODB_FORCE_EMPTY',2); 81 define('ADODB_FORCE_VALUE',3); 82 //********************************************************// 83 84 85 if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) { 86 87 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>'); 88 89 // allow [ ] @ ` " and . in table names 90 define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)'); 91 92 // prefetching used by oracle 93 if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10); 94 95 96 /* 97 Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names. 98 This currently works only with mssql, odbc, oci8po and ibase derived drivers. 99 100 0 = assoc lowercase field names. $rs->fields['orderid'] 101 1 = assoc uppercase field names. $rs->fields['ORDERID'] 102 2 = use native-case field names. $rs->fields['OrderID'] 103 */ 104 105 define('ADODB_FETCH_DEFAULT',0); 106 define('ADODB_FETCH_NUM',1); 107 define('ADODB_FETCH_ASSOC',2); 108 define('ADODB_FETCH_BOTH',3); 109 110 if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100); 111 112 // PHP's version scheme makes converting to numbers difficult - workaround 113 $_adodb_ver = (float) PHP_VERSION; 114 if ($_adodb_ver >= 5.0) { 115 define('ADODB_PHPVER',0x5000); 116 } else if ($_adodb_ver > 4.299999) { # 4.3 117 define('ADODB_PHPVER',0x4300); 118 } else if ($_adodb_ver > 4.199999) { # 4.2 119 define('ADODB_PHPVER',0x4200); 120 } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) { 121 define('ADODB_PHPVER',0x4050); 122 } else { 123 define('ADODB_PHPVER',0x4000); 124 } 125 } 126 127 //if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); 128 129 130 /** 131 Accepts $src and $dest arrays, replacing string $data 132 */ 133 function ADODB_str_replace($src, $dest, $data) 134 { 135 if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data); 136 137 $s = reset($src); 138 $d = reset($dest); 139 while ($s !== false) { 140 $data = str_replace($s,$d,$data); 141 $s = next($src); 142 $d = next($dest); 143 } 144 return $data; 145 } 146 147 function ADODB_Setup() 148 { 149 GLOBAL 150 $ADODB_vers, // database version 151 $ADODB_COUNTRECS, // count number of records returned - slows down query 152 $ADODB_CACHE_DIR, // directory to cache recordsets 153 $ADODB_FETCH_MODE, 154 $ADODB_FORCE_TYPE; 155 156 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT; 157 $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE; 158 159 160 if (!isset($ADODB_CACHE_DIR)) { 161 $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp'; 162 } else { 163 // do not accept url based paths, eg. http:/ or ftp:/ 164 if (strpos($ADODB_CACHE_DIR,'://') !== false) 165 die("Illegal path http:// or ftp://"); 166 } 167 168 169 // Initialize random number generator for randomizing cache flushes 170 srand(((double)microtime())*1000000); 171 172 /** 173 * ADODB version as a string. 174 */ 175 $ADODB_vers = 'V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.'; 176 177 /** 178 * Determines whether recordset->RecordCount() is used. 179 * Set to false for highest performance -- RecordCount() will always return -1 then 180 * for databases that provide "virtual" recordcounts... 181 */ 182 if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true; 183 } 184 185 186 //============================================================================================== 187 // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB 188 //============================================================================================== 189 190 ADODB_Setup(); 191 192 //============================================================================================== 193 // CLASS ADOFieldObject 194 //============================================================================================== 195 /** 196 * Helper class for FetchFields -- holds info on a column 197 */ 198 class ADOFieldObject { 199 var $name = ''; 200 var $max_length=0; 201 var $type=""; 202/* 203 // additional fields by dannym... (danny_milo@yahoo.com) 204 var $not_null = false; 205 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^ 206 // so we can as well make not_null standard (leaving it at "false" does not harm anyways) 207 208 var $has_default = false; // this one I have done only in mysql and postgres for now ... 209 // others to come (dannym) 210 var $default_value; // default, if any, and supported. Check has_default first. 211*/ 212 } 213 214 215 216 function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) 217 { 218 //print "Errorno ($fn errno=$errno m=$errmsg) "; 219 $thisConnection->_transOK = false; 220 if ($thisConnection->_oldRaiseFn) { 221 $fn = $thisConnection->_oldRaiseFn; 222 $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection); 223 } 224 } 225 226 //============================================================================================== 227 // CLASS ADOConnection 228 //============================================================================================== 229 230 /** 231 * Connection object. For connecting to databases, and executing queries. 232 */ 233 class ADOConnection { 234 // 235 // PUBLIC VARS 236 // 237 var $dataProvider = 'native'; 238 var $databaseType = ''; /// RDBMS currently in use, eg. odbc, mysql, mssql 239 var $database = ''; /// Name of database to be used. 240 var $host = ''; /// The hostname of the database server 241 var $user = ''; /// The username which is used to connect to the database server. 242 var $password = ''; /// Password for the username. For security, we no longer store it. 243 var $debug = false; /// if set to true will output sql statements 244 var $maxblobsize = 262144; /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro 245 var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase 246 var $substr = 'substr'; /// substring operator 247 var $length = 'length'; /// string length ofperator 248 var $random = 'rand()'; /// random function 249 var $upperCase = 'upper'; /// uppercase function 250 var $fmtDate = "'Y-m-d'"; /// used by DBDate() as the default date format used by the database 251 var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt. 252 var $true = '1'; /// string that represents TRUE for a database 253 var $false = '0'; /// string that represents FALSE for a database 254 var $replaceQuote = "\\'"; /// string to use to replace quotes 255 var $nameQuote = '"'; /// string to use to quote identifiers and names 256 var $charSet=false; /// character set to use - only for interbase, postgres and oci8 257 var $metaDatabasesSQL = ''; 258 var $metaTablesSQL = ''; 259 var $uniqueOrderBy = false; /// All order by columns have to be unique 260 var $emptyDate = ' '; 261 var $emptyTimeStamp = ' '; 262 var $lastInsID = false; 263 //-- 264 var $hasInsertID = false; /// supports autoincrement ID? 265 var $hasAffectedRows = false; /// supports affected rows for update/delete? 266 var $hasTop = false; /// support mssql/access SELECT TOP 10 * FROM TABLE 267 var $hasLimit = false; /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10 268 var $readOnly = false; /// this is a readonly database - used by phpLens 269 var $hasMoveFirst = false; /// has ability to run MoveFirst(), scrolling backwards 270 var $hasGenID = false; /// can generate sequences using GenID(); 271 var $hasTransactions = true; /// has transactions 272 //-- 273 var $genID = 0; /// sequence id used by GenID(); 274 var $raiseErrorFn = false; /// error function to call 275 var $isoDates = false; /// accepts dates in ISO format 276 var $cacheSecs = 3600; /// cache for 1 hour 277 var $sysDate = false; /// name of function that returns the current date 278 var $sysTimeStamp = false; /// name of function that returns the current timestamp 279 var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets 280 281 var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' ' 282 var $numCacheHits = 0; 283 var $numCacheMisses = 0; 284 var $pageExecuteCountRows = true; 285 var $uniqueSort = false; /// indicates that all fields in order by must be unique 286 var $leftOuter = false; /// operator to use for left outer join in WHERE clause 287 var $rightOuter = false; /// operator to use for right outer join in WHERE clause 288 var $ansiOuter = false; /// whether ansi outer join syntax supported 289 var $autoRollback = false; // autoRollback on PConnect(). 290 var $poorAffectedRows = false; // affectedRows not working or unreliable 291 292 var $fnExecute = false; 293 var $fnCacheExecute = false; 294 var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char 295 var $rsPrefix = "ADORecordSet_"; 296 297 var $autoCommit = true; /// do not modify this yourself - actually private 298 var $transOff = 0; /// temporarily disable transactions 299 var $transCnt = 0; /// count of nested transactions 300 301 var $fetchMode=false; 302 // 303 // PRIVATE VARS 304 // 305 var $_oldRaiseFn = false; 306 var $_transOK = null; 307 var $_connectionID = false; /// The returned link identifier whenever a successful database connection is made. 308 var $_errorMsg = false; /// A variable which was used to keep the returned last error message. The value will 309 /// then returned by the errorMsg() function 310 var $_errorCode = false; /// Last error code, not guaranteed to be used - only by oci8 311 var $_queryID = false; /// This variable keeps the last created result link identifier 312 313 var $_isPersistentConnection = false; /// A boolean variable to state whether its a persistent connection or normal connection. */ 314 var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters. 315 var $_evalAll = false; 316 var $_affected = false; 317 var $_logsql = false; 318 319 320 321 /** 322 * Constructor 323 */ 324 function ADOConnection() 325 { 326 die('Virtual Class -- cannot instantiate'); 327 } 328 329 function Version() 330 { 331 global $ADODB_vers; 332 333 return (float) substr($ADODB_vers,1); 334 } 335 336 /** 337 Get server version info... 338 339 @returns An array with 2 elements: $arr['string'] is the description string, 340 and $arr[version] is the version (also a string). 341 */ 342 function ServerInfo() 343 { 344 return array('description' => '', 'version' => ''); 345 } 346 347 function IsConnected() 348 { 349 return !empty($this->_connectionID); 350 } 351 352 function _findvers($str) 353 { 354 if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1]; 355 else return ''; 356 } 357 358 /** 359 * All error messages go through this bottleneck function. 360 * You can define your own handler by defining the function name in ADODB_OUTP. 361 */ 362 function outp($msg,$newline=true) 363 { 364 global $ADODB_FLUSH,$ADODB_OUTP; 365 366 if (defined('ADODB_OUTP')) { 367 $fn = ADODB_OUTP; 368 $fn($msg,$newline); 369 return; 370 } else if (isset($ADODB_OUTP)) { 371 $fn = $ADODB_OUTP; 372 $fn($msg,$newline); 373 return; 374 } 375 376 if ($newline) $msg .= "<br>\n"; 377 378 if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg; 379 else echo strip_tags($msg); 380 381 382 if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); // do not flush if output buffering enabled - useless - thx to Jesse Mullan 383 384 } 385 386 function Time() 387 { 388 $rs =& $this->_Execute("select $this->sysTimeStamp"); 389 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields)); 390 391 return false; 392 } 393 394 /** 395 * Connect to database 396 * 397 * @param [argHostname] Host to connect to 398 * @param [argUsername] Userid to login 399 * @param [argPassword] Associated password 400 * @param [argDatabaseName] database 401 * @param [forceNew] force new connection 402 * 403 * @return true or false 404 */ 405 function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) 406 { 407 if ($argHostname != "") $this->host = $argHostname; 408 if ($argUsername != "") $this->user = $argUsername; 409 if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons 410 if ($argDatabaseName != "") $this->database = $argDatabaseName; 411 412 $this->_isPersistentConnection = false; 413 if ($forceNew) { 414 if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true; 415 } else { 416 if ($rez=$this->_connect($this->host, $this->user, $this->password, $this->database)) return true; 417 } 418 if (isset($rez)) { 419 $err = $this->ErrorMsg(); 420 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'"; 421 $ret = false; 422 } else { 423 $err = "Missing extension for ".$this->dataProvider; 424 $ret = 0; 425 } 426 if ($fn = $this->raiseErrorFn) 427 $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); 428 429 430 $this->_connectionID = false; 431 if ($this->debug) ADOConnection::outp( $this->host.': '.$err); 432 return $ret; 433 } 434 435 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) 436 { 437 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName); 438 } 439 440 441 /** 442 * Always force a new connection to database - currently only works with oracle 443 * 444 * @param [argHostname] Host to connect to 445 * @param [argUsername] Userid to login 446 * @param [argPassword] Associated password 447 * @param [argDatabaseName] database 448 * 449 * @return true or false 450 */ 451 function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") 452 { 453 return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true); 454 } 455 456 /** 457 * Establish persistent connect to database 458 * 459 * @param [argHostname] Host to connect to 460 * @param [argUsername] Userid to login 461 * @param [argPassword] Associated password 462 * @param [argDatabaseName] database 463 * 464 * @return return true or false 465 */ 466 function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") 467 { 468 if (defined('ADODB_NEVER_PERSIST')) 469 return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName); 470 471 if ($argHostname != "") $this->host = $argHostname; 472 if ($argUsername != "") $this->user = $argUsername; 473 if ($argPassword != "") $this->password = $argPassword; 474 if ($argDatabaseName != "") $this->database = $argDatabaseName; 475 476 $this->_isPersistentConnection = true; 477 if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true; 478 if (isset($rez)) { 479 $err = $this->ErrorMsg(); 480 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'"; 481 $ret = false; 482 } else { 483 $err = "Missing extension for ".$this->dataProvider; 484 $ret = 0; 485 } 486 if ($fn = $this->raiseErrorFn) { 487 $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); 488 } 489 490 $this->_connectionID = false; 491 if ($this->debug) ADOConnection::outp( $this->host.': '.$err); 492 return $ret; 493 } 494 495 // Format date column in sql string given an input format that understands Y M D 496 function SQLDate($fmt, $col=false) 497 { 498 if (!$col) $col = $this->sysDate; 499 return $col; // child class implement 500 } 501 502 /** 503 * Should prepare the sql statement and return the stmt resource. 504 * For databases that do not support this, we return the $sql. To ensure 505 * compatibility with databases that do not support prepare: 506 * 507 * $stmt = $db->Prepare("insert into table (id, name) values (?,?)"); 508 * $db->Execute($stmt,array(1,'Jill')) or die('insert failed'); 509 * $db->Execute($stmt,array(2,'Joe')) or die('insert failed'); 510 * 511 * @param sql SQL to send to database 512 * 513 * @return return FALSE, or the prepared statement, or the original sql if 514 * if the database does not support prepare. 515 * 516 */ 517 function Prepare($sql) 518 { 519 return $sql; 520 } 521 522 /** 523 * Some databases, eg. mssql require a different function for preparing 524 * stored procedures. So we cannot use Prepare(). 525 * 526 * Should prepare the stored procedure and return the stmt resource. 527 * For databases that do not support this, we return the $sql. To ensure 528 * compatibility with databases that do not support prepare: 529 * 530 * @param sql SQL to send to database 531 * 532 * @return return FALSE, or the prepared statement, or the original sql if 533 * if the database does not support prepare. 534 * 535 */ 536 function PrepareSP($sql,$param=true) 537 { 538 return $this->Prepare($sql,$param); 539 } 540 541 /** 542 * PEAR DB Compat 543 */ 544 function Quote($s) 545 { 546 return $this->qstr($s,false); 547 } 548 549 /** 550 Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de> 551 */ 552 function QMagic($s) 553 { 554 return $this->qstr($s,get_magic_quotes_gpc()); 555 } 556 557 function q(&$s) 558 { 559 $s = $this->qstr($s,false); 560 } 561 562 /** 563 * PEAR DB Compat - do not use internally. 564 */ 565 function ErrorNative() 566 { 567 return $this->ErrorNo(); 568 } 569 570 571 /** 572 * PEAR DB Compat - do not use internally. 573 */ 574 function nextId($seq_name) 575 { 576 return $this->GenID($seq_name); 577 } 578 579 /** 580 * Lock a row, will escalate and lock the table if row locking not supported 581 * will normally free the lock at the end of the transaction 582 * 583 * @param $table name of table to lock 584 * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock 585 */ 586 function RowLock($table,$where) 587 { 588 return false; 589 } 590 591 function CommitLock($table) 592 { 593 return $this->CommitTrans(); 594 } 595 596 function RollbackLock($table) 597 { 598 return $this->RollbackTrans(); 599 } 600 601 /** 602 * PEAR DB Compat - do not use internally. 603 * 604 * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical 605 * for easy porting :-) 606 * 607 * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM 608 * @returns The previous fetch mode 609 */ 610 function SetFetchMode($mode) 611 { 612 $old = $this->fetchMode; 613 $this->fetchMode = $mode; 614 615 if ($old === false) { 616 global $ADODB_FETCH_MODE; 617 return $ADODB_FETCH_MODE; 618 } 619 return $old; 620 } 621 622 623 /** 624 * PEAR DB Compat - do not use internally. 625 */ 626 function &Query($sql, $inputarr=false) 627 { 628 $rs = &$this->Execute($sql, $inputarr); 629 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); 630 return $rs; 631 } 632 633 634 /** 635 * PEAR DB Compat - do not use internally 636 */ 637 function &LimitQuery($sql, $offset, $count, $params=false) 638 { 639 $rs = &$this->SelectLimit($sql, $count, $offset, $params); 640 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); 641 return $rs; 642 } 643 644 645 /** 646 * PEAR DB Compat - do not use internally 647 */ 648 function Disconnect() 649 { 650 return $this->Close(); 651 } 652 653 /* 654 Returns placeholder for parameter, eg. 655 $DB->Param('a') 656 657 will return ':a' for Oracle, and '?' for most other databases... 658 659 For databases that require positioned params, eg $1, $2, $3 for postgresql, 660 pass in Param(false) before setting the first parameter. 661 */ 662 function Param($name,$type='C') 663 { 664 return '?'; 665 } 666 667 /* 668 InParameter and OutParameter are self-documenting versions of Parameter(). 669 */ 670 function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) 671 { 672 return $this->Parameter($stmt,$var,$name,false,$maxLen,$type); 673 } 674 675 /* 676 */ 677 function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) 678 { 679 return $this->Parameter($stmt,$var,$name,true,$maxLen,$type); 680 681 } 682 683 /* 684 Usage in oracle 685 $stmt = $db->Prepare('select * from table where id =:myid and group=:group'); 686 $db->Parameter($stmt,$id,'myid'); 687 $db->Parameter($stmt,$group,'group',64); 688 $db->Execute(); 689 690 @param $stmt Statement returned by Prepare() or PrepareSP(). 691 @param $var PHP variable to bind to 692 @param $name Name of stored procedure variable name to bind to. 693 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. 694 @param [$maxLen] Holds an maximum length of the variable. 695 @param [$type] The data type of $var. Legal values depend on driver. 696 697 */ 698 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) 699 { 700 return false; 701 } 702 703 /** 704 Improved method of initiating a transaction. Used together with CompleteTrans(). 705 Advantages include: 706 707 a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans. 708 Only the outermost block is treated as a transaction.<br> 709 b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br> 710 c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block 711 are disabled, making it backward compatible. 712 */ 713 function StartTrans($errfn = 'ADODB_TransMonitor') 714 { 715 if ($this->transOff > 0) { 716 $this->transOff += 1; 717 return; 718 } 719 720 $this->_oldRaiseFn = $this->raiseErrorFn; 721 $this->raiseErrorFn = $errfn; 722 $this->_transOK = true; 723 724 if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); 725 $this->BeginTrans(); 726 $this->transOff = 1; 727 } 728 729 730 /** 731 Used together with StartTrans() to end a transaction. Monitors connection 732 for sql errors, and will commit or rollback as appropriate. 733 734 @autoComplete if true, monitor sql errors and commit and rollback as appropriate, 735 and if set to false force rollback even if no SQL error detected. 736 @returns true on commit, false on rollback. 737 */ 738 function CompleteTrans($autoComplete = true) 739 { 740 if ($this->transOff > 1) { 741 $this->transOff -= 1; 742 return true; 743 } 744 $this->raiseErrorFn = $this->_oldRaiseFn; 745 746 $this->transOff = 0; 747 if ($this->_transOK && $autoComplete) { 748 if (!$this->CommitTrans()) { 749 $this->_transOK = false; 750 if ($this->debug) ADOConnection::outp("Smart Commit failed"); 751 } else 752 if ($this->debug) ADOConnection::outp("Smart Commit occurred"); 753 } else { 754 $this->_transOK = false; 755 $this->RollbackTrans(); 756 if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred"); 757 } 758 759 return $this->_transOK; 760 } 761 762 /* 763 At the end of a StartTrans/CompleteTrans block, perform a rollback. 764 */ 765 function FailTrans() 766 { 767 if ($this->debug) 768 if ($this->transOff == 0) { 769 ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); 770 } else { 771 ADOConnection::outp("FailTrans was called"); 772 adodb_backtrace(); 773 } 774 $this->_transOK = false; 775 } 776 777 /** 778 Check if transaction has failed, only for Smart Transactions. 779 */ 780 function HasFailedTrans() 781 { 782 if ($this->transOff > 0) return $this->_transOK == false; 783 return false; 784 } 785 786 /** 787 * Execute SQL 788 * 789 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text) 790 * @param [inputarr] holds the input data to bind to. Null elements will be set to null. 791 * @return RecordSet or false 792 */ 793 function &Execute($sql,$inputarr=false) 794 { 795 if ($this->fnExecute) { 796 $fn = $this->fnExecute; 797 $ret =& $fn($this,$sql,$inputarr); 798 if (isset($ret)) return $ret; 799 } 800 if ($inputarr) { 801 if (!is_array($inputarr)) $inputarr = array($inputarr); 802 803 $element0 = reset($inputarr); 804 # is_object check because oci8 descriptors can be passed in 805 $array_2d = is_array($element0) && !is_object(reset($element0)); 806 807 if (!is_array($sql) && !$this->_bindInputArray) { 808 $sqlarr = explode('?',$sql); 809 810 if (!$array_2d) $inputarr = array($inputarr); 811 foreach($inputarr as $arr) { 812 $sql = ''; $i = 0; 813 foreach($arr as $v) { 814 $sql .= $sqlarr[$i]; 815 // from Ron Baldwin <ron.baldwin#sourceprose.com> 816 // Only quote string types 817 $typ = gettype($v); 818 if ($typ == 'string') 819 $sql .= $this->qstr($v); 820 else if ($typ == 'double') 821 $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1 822 else if ($typ == 'boolean') 823 $sql .= $v ? $this->true : $this->false; 824 else if ($v === null) 825 $sql .= 'NULL'; 826 else 827 $sql .= $v; 828 $i += 1; 829 } 830 if (isset($sqlarr[$i])) { 831 $sql .= $sqlarr[$i]; 832 if ($i+1 != sizeof($sqlarr)) ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql)); 833 } else if ($i != sizeof($sqlarr)) 834 ADOConnection::outp( "Input array does not match ?: ".htmlspecialchars($sql)); 835 836 $ret =& $this->_Execute($sql); 837 if (!$ret) return $ret; 838 } 839 } else { 840 if ($array_2d) { 841 if (is_string($sql)) 842 $stmt = $this->Prepare($sql); 843 else 844 $stmt = $sql; 845 846 foreach($inputarr as $arr) { 847 $ret =& $this->_Execute($stmt,$arr); 848 if (!$ret) return $ret; 849 } 850 } else { 851 $ret =& $this->_Execute($sql,$inputarr); 852 } 853 } 854 } else { 855 $ret =& $this->_Execute($sql,false); 856 } 857 858 return $ret; 859 } 860 861 862 function &_Execute($sql,$inputarr=false) 863 { 864 865 if ($this->debug) { 866 global $ADODB_INCLUDED_LIB; 867 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 868 $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr); 869 } else { 870 $this->_queryID = @$this->_query($sql,$inputarr); 871 } 872 873 /************************ 874 // OK, query executed 875 *************************/ 876 877 if ($this->_queryID === false) { // error handling if query fails 878 if ($this->debug == 99) adodb_backtrace(true,5); 879 $fn = $this->raiseErrorFn; 880 if ($fn) { 881 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this); 882 } 883 $false = false; 884 return $false; 885 } 886 887 if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead 888 $rs =& new ADORecordSet_empty(); 889 return $rs; 890 } 891 892 // return real recordset from select statement 893 $rsclass = $this->rsPrefix.$this->databaseType; 894 $rs = new $rsclass($this->_queryID,$this->fetchMode); 895 $rs->connection = &$this; // Pablo suggestion 896 $rs->Init(); 897 if (is_array($sql)) $rs->sql = $sql[0]; 898 else $rs->sql = $sql; 899 if ($rs->_numOfRows <= 0) { 900 global $ADODB_COUNTRECS; 901 if ($ADODB_COUNTRECS) { 902 if (!$rs->EOF) { 903 $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql)); 904 $rs->_queryID = $this->_queryID; 905 } else 906 $rs->_numOfRows = 0; 907 } 908 } 909 return $rs; 910 } 911 912 function CreateSequence($seqname='adodbseq',$startID=1) 913 { 914 if (empty($this->_genSeqSQL)) return false; 915 return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); 916 } 917 918 function DropSequence($seqname='adodbseq') 919 { 920 if (empty($this->_dropSeqSQL)) return false; 921 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); 922 } 923 924 /** 925 * Generates a sequence id and stores it in $this->genID; 926 * GenID is only available if $this->hasGenID = true; 927 * 928 * @param seqname name of sequence to use 929 * @param startID if sequence does not exist, start at this ID 930 * @return 0 if not supported, otherwise a sequence id 931 */ 932 function GenID($seqname='adodbseq',$startID=1) 933 { 934 if (!$this->hasGenID) { 935 return 0; // formerly returns false pre 1.60 936 } 937 938 $getnext = sprintf($this->_genIDSQL,$seqname); 939 940 $holdtransOK = $this->_transOK; 941 942 $save_handler = $this->raiseErrorFn; 943 $this->raiseErrorFn = ''; 944 @($rs = $this->Execute($getnext)); 945 $this->raiseErrorFn = $save_handler; 946 947 if (!$rs) { 948 $this->_transOK = $holdtransOK; //if the status was ok before reset 949 $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); 950 $rs = $this->Execute($getnext); 951 } 952 if ($rs && !$rs->EOF) $this->genID = reset($rs->fields); 953 else $this->genID = 0; // false 954 955 if ($rs) $rs->Close(); 956 957 return $this->genID; 958 } 959 960 /** 961 * @param $table string name of the table, not needed by all databases (eg. mysql), default '' 962 * @param $column string name of the column, not needed by all databases (eg. mysql), default '' 963 * @return the last inserted ID. Not all databases support this. 964 */ 965 function Insert_ID($table='',$column='') 966 { 967 if ($this->_logsql && $this->lastInsID) return $this->lastInsID; 968 if ($this->hasInsertID) return $this->_insertid($table,$column); 969 if ($this->debug) { 970 ADOConnection::outp( '<p>Insert_ID error</p>'); 971 adodb_backtrace(); 972 } 973 return false; 974 } 975 976 977 /** 978 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org> 979 * 980 * @return the last inserted ID. All databases support this. But aware possible 981 * problems in multiuser environments. Heavy test this before deploying. 982 */ 983 function PO_Insert_ID($table="", $id="") 984 { 985 if ($this->hasInsertID){ 986 return $this->Insert_ID($table,$id); 987 } else { 988 return $this->GetOne("SELECT MAX($id) FROM $table"); 989 } 990 } 991 992 /** 993 * @return # rows affected by UPDATE/DELETE 994 */ 995 function Affected_Rows() 996 { 997 if ($this->hasAffectedRows) { 998 if ($this->fnExecute === 'adodb_log_sql') { 999 if ($this->_logsql && $this->_affected !== false) return $this->_affected; 1000 } 1001 $val = $this->_affectedrows(); 1002 return ($val < 0) ? false : $val; 1003 } 1004 1005 if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false); 1006 return false; 1007 } 1008 1009 1010 /** 1011 * @return the last error message 1012 */ 1013 function ErrorMsg() 1014 { 1015 if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg; 1016 else return ''; 1017 } 1018 1019 1020 /** 1021 * @return the last error number. Normally 0 means no error. 1022 */ 1023 function ErrorNo() 1024 { 1025 return ($this->_errorMsg) ? -1 : 0; 1026 } 1027 1028 function MetaError($err=false) 1029 { 1030 include_once(ADODB_DIR."/adodb-error.inc.php"); 1031 if ($err === false) $err = $this->ErrorNo(); 1032 return adodb_error($this->dataProvider,$this->databaseType,$err); 1033 } 1034 1035 function MetaErrorMsg($errno) 1036 { 1037 include_once(ADODB_DIR."/adodb-error.inc.php"); 1038 return adodb_errormsg($errno); 1039 } 1040 1041 /** 1042 * @returns an array with the primary key columns in it. 1043 */ 1044 function MetaPrimaryKeys($table, $owner=false) 1045 { 1046 // owner not used in base class - see oci8 1047 $p = array(); 1048 $objs =& $this->MetaColumns($table); 1049 if ($objs) { 1050 foreach($objs as $v) { 1051 if (!empty($v->primary_key)) 1052 $p[] = $v->name; 1053 } 1054 } 1055 if (sizeof($p)) return $p; 1056 if (function_exists('ADODB_VIEW_PRIMARYKEYS')) 1057 return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); 1058 return false; 1059 } 1060 1061 /** 1062 * @returns assoc array where keys are tables, and values are foreign keys 1063 */ 1064 function MetaForeignKeys($table, $owner=false, $upper=false) 1065 { 1066 return false; 1067 } 1068 /** 1069 * Choose a database to connect to. Many databases do not support this. 1070 * 1071 * @param dbName is the name of the database to select 1072 * @return true or false 1073 */ 1074 function SelectDB($dbName) 1075 {return false;} 1076 1077 1078 /** 1079 * Will select, getting rows from $offset (1-based), for $nrows. 1080 * This simulates the MySQL "select * from table limit $offset,$nrows" , and 1081 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that 1082 * MySQL and PostgreSQL parameter ordering is the opposite of the other. 1083 * eg. 1084 * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based) 1085 * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based) 1086 * 1087 * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set) 1088 * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set 1089 * 1090 * @param sql 1091 * @param [offset] is the row to start calculations from (1-based) 1092 * @param [nrows] is the number of rows to get 1093 * @param [inputarr] array of bind variables 1094 * @param [secs2cache] is a private parameter only used by jlim 1095 * @return the recordset ($rs->databaseType == 'array') 1096 */ 1097 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) 1098 { 1099 if ($this->hasTop && $nrows > 0) { 1100 // suggested by Reinhard Balling. Access requires top after distinct 1101 // Informix requires first before distinct - F Riosa 1102 $ismssql = (strpos($this->databaseType,'mssql') !== false); 1103 if ($ismssql) $isaccess = false; 1104 else $isaccess = (strpos($this->databaseType,'access') !== false); 1105 1106 if ($offset <= 0) { 1107 1108 // access includes ties in result 1109 if ($isaccess) { 1110 $sql = preg_replace( 1111 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); 1112 1113 if ($secs2cache>0) { 1114 $ret =& $this->CacheExecute($secs2cache, $sql,$inputarr); 1115 } else { 1116 $ret =& $this->Execute($sql,$inputarr); 1117 } 1118 return $ret; // PHP5 fix 1119 } else if ($ismssql){ 1120 $sql = preg_replace( 1121 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); 1122 } else { 1123 $sql = preg_replace( 1124 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); 1125 } 1126 } else { 1127 $nn = $nrows + $offset; 1128 if ($isaccess || $ismssql) { 1129 $sql = preg_replace( 1130 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); 1131 } else { 1132 $sql = preg_replace( 1133 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); 1134 } 1135 } 1136 } 1137 1138 // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows 1139 // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS. 1140 global $ADODB_COUNTRECS; 1141 1142 $savec = $ADODB_COUNTRECS; 1143 $ADODB_COUNTRECS = false; 1144 1145 if ($offset>0){ 1146 if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); 1147 else $rs = &$this->Execute($sql,$inputarr); 1148 } else { 1149 if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); 1150 else $rs = &$this->Execute($sql,$inputarr); 1151 } 1152 $ADODB_COUNTRECS = $savec; 1153 if ($rs && !$rs->EOF) { 1154 $rs =& $this->_rs2rs($rs,$nrows,$offset); 1155 } 1156 //print_r($rs); 1157 return $rs; 1158 } 1159 1160 /** 1161 * Create serializable recordset. Breaks rs link to connection. 1162 * 1163 * @param rs the recordset to serialize 1164 */ 1165 function &SerializableRS(&$rs) 1166 { 1167 $rs2 =& $this->_rs2rs($rs); 1168 $ignore = false; 1169 $rs2->connection =& $ignore; 1170 1171 return $rs2; 1172 } 1173 1174 /** 1175 * Convert database recordset to an array recordset 1176 * input recordset's cursor should be at beginning, and 1177 * old $rs will be closed. 1178 * 1179 * @param rs the recordset to copy 1180 * @param [nrows] number of rows to retrieve (optional) 1181 * @param [offset] offset by number of rows (optional) 1182 * @return the new recordset 1183 */ 1184 function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) 1185 { 1186 if (! $rs) { 1187 $false = false; 1188 return $false; 1189 } 1190 $dbtype = $rs->databaseType; 1191 if (!$dbtype) { 1192 $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ? 1193 return $rs; 1194 } 1195 if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) { 1196 $rs->MoveFirst(); 1197 $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ? 1198 return $rs; 1199 } 1200 $flds = array(); 1201 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { 1202 $flds[] = $rs->FetchField($i); 1203 } 1204 1205 $arr =& $rs->GetArrayLimit($nrows,$offset); 1206 //print_r($arr); 1207 if ($close) $rs->Close(); 1208 1209 $arrayClass = $this->arrayClass; 1210 1211 $rs2 = new $arrayClass(); 1212 $rs2->connection = &$this; 1213 $rs2->sql = $rs->sql; 1214 $rs2->dataProvider = $this->dataProvider; 1215 $rs2->InitArrayFields($arr,$flds); 1216 $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode; 1217 return $rs2; 1218 } 1219 1220 /* 1221 * Return all rows. Compat with PEAR DB 1222 */ 1223 function &GetAll($sql, $inputarr=false) 1224 { 1225 $arr =& $this->GetArray($sql,$inputarr); 1226 return $arr; 1227 } 1228 1229 function &GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false) 1230 { 1231 $rs =& $this->Execute($sql, $inputarr); 1232 if (!$rs) { 1233 $false = false; 1234 return $false; 1235 } 1236 $arr =& $rs->GetAssoc($force_array,$first2cols); 1237 return $arr; 1238 } 1239 1240 function &CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false) 1241 { 1242 if (!is_numeric($secs2cache)) { 1243 $first2cols = $force_array; 1244 $force_array = $inputarr; 1245 } 1246 $rs =& $this->CacheExecute($secs2cache, $sql, $inputarr); 1247 if (!$rs) { 1248 $false = false; 1249 return $false; 1250 } 1251 $arr =& $rs->GetAssoc($force_array,$first2cols); 1252 return $arr; 1253 } 1254 1255 /** 1256 * Return first element of first row of sql statement. Recordset is disposed 1257 * for you. 1258 * 1259 * @param sql SQL statement 1260 * @param [inputarr] input bind array 1261 */ 1262 function GetOne($sql,$inputarr=false) 1263 { 1264 global $ADODB_COUNTRECS; 1265 $crecs = $ADODB_COUNTRECS; 1266 $ADODB_COUNTRECS = false; 1267 1268 $ret = false; 1269 $rs = &$this->Execute($sql,$inputarr); 1270 if ($rs) { 1271 if (!$rs->EOF) $ret = reset($rs->fields); 1272 $rs->Close(); 1273 } 1274 $ADODB_COUNTRECS = $crecs; 1275 return $ret; 1276 } 1277 1278 function CacheGetOne($secs2cache,$sql=false,$inputarr=false) 1279 { 1280 $ret = false; 1281 $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); 1282 if ($rs) { 1283 if (!$rs->EOF) $ret = reset($rs->fields); 1284 $rs->Close(); 1285 } 1286 1287 return $ret; 1288 } 1289 1290 function GetCol($sql, $inputarr = false, $trim = false) 1291 { 1292 $rv = false; 1293 $rs = &$this->Execute($sql, $inputarr); 1294 if ($rs) { 1295 $rv = array(); 1296 if ($trim) { 1297 while (!$rs->EOF) { 1298 $rv[] = trim(reset($rs->fields)); 1299 $rs->MoveNext(); 1300 } 1301 } else { 1302 while (!$rs->EOF) { 1303 $rv[] = reset($rs->fields); 1304 $rs->MoveNext(); 1305 } 1306 } 1307 $rs->Close(); 1308 } 1309 return $rv; 1310 } 1311 1312 function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) 1313 { 1314 $rv = false; 1315 $rs = &$this->CacheExecute($secs, $sql, $inputarr); 1316 if ($rs) { 1317 if ($trim) { 1318 while (!$rs->EOF) { 1319 $rv[] = trim(reset($rs->fields)); 1320 $rs->MoveNext(); 1321 } 1322 } else { 1323 while (!$rs->EOF) { 1324 $rv[] = reset($rs->fields); 1325 $rs->MoveNext(); 1326 } 1327 } 1328 $rs->Close(); 1329 } 1330 return $rv; 1331 } 1332 1333 /* 1334 Calculate the offset of a date for a particular database and generate 1335 appropriate SQL. Useful for calculating future/past dates and storing 1336 in a database. 1337 1338 If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour. 1339 */ 1340 function OffsetDate($dayFraction,$date=false) 1341 { 1342 if (!$date) $date = $this->sysDate; 1343 return '('.$date.'+'.$dayFraction.')'; 1344 } 1345 1346 1347 /** 1348 * 1349 * @param sql SQL statement 1350 * @param [inputarr] input bind array 1351 */ 1352 function &GetArray($sql,$inputarr=false) 1353 { 1354 global $ADODB_COUNTRECS; 1355 1356 $savec = $ADODB_COUNTRECS; 1357 $ADODB_COUNTRECS = false; 1358 $rs =& $this->Execute($sql,$inputarr); 1359 $ADODB_COUNTRECS = $savec; 1360 if (!$rs) 1361 if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); 1362 else { 1363 $false = false; 1364 return $false; 1365 } 1366 $arr =& $rs->GetArray(); 1367 $rs->Close(); 1368 return $arr; 1369 } 1370 1371 function &CacheGetAll($secs2cache,$sql=false,$inputarr=false) 1372 { 1373 return $this->CacheGetArray($secs2cache,$sql,$inputarr); 1374 } 1375 1376 function &CacheGetArray($secs2cache,$sql=false,$inputarr=false) 1377 { 1378 global $ADODB_COUNTRECS; 1379 1380 $savec = $ADODB_COUNTRECS; 1381 $ADODB_COUNTRECS = false; 1382 $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr); 1383 $ADODB_COUNTRECS = $savec; 1384 1385 if (!$rs) 1386 if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); 1387 else { 1388 $false = false; 1389 return $false; 1390 } 1391 $arr =& $rs->GetArray(); 1392 $rs->Close(); 1393 return $arr; 1394 } 1395 1396 1397 1398 /** 1399 * Return one row of sql statement. Recordset is disposed for you. 1400 * 1401 * @param sql SQL statement 1402 * @param [inputarr] input bind array 1403 */ 1404 function &GetRow($sql,$inputarr=false) 1405 { 1406 global $ADODB_COUNTRECS; 1407 $crecs = $ADODB_COUNTRECS; 1408 $ADODB_COUNTRECS = false; 1409 1410 $rs =& $this->Execute($sql,$inputarr); 1411 1412 $ADODB_COUNTRECS = $crecs; 1413 if ($rs) { 1414 if (!$rs->EOF) $arr = $rs->fields; 1415 else $arr = array(); 1416 $rs->Close(); 1417 return $arr; 1418 } 1419 1420 $false = false; 1421 return $false; 1422 } 1423 1424 function &CacheGetRow($secs2cache,$sql=false,$inputarr=false) 1425 { 1426 $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr); 1427 if ($rs) { 1428 $arr = false; 1429 if (!$rs->EOF) $arr = $rs->fields; 1430 $rs->Close(); 1431 return $arr; 1432 } 1433 $false = false; 1434 return $false; 1435 } 1436 1437 /** 1438 * Insert or replace a single record. Note: this is not the same as MySQL's replace. 1439 * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL. 1440 * Also note that no table locking is done currently, so it is possible that the 1441 * record be inserted twice by two programs... 1442 * 1443 * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname'); 1444 * 1445 * $table table name 1446 * $fieldArray associative array of data (you must quote strings yourself). 1447 * $keyCol the primary key field name or if compound key, array of field names 1448 * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers 1449 * but does not work with dates nor SQL functions. 1450 * has_autoinc the primary key is an auto-inc field, so skip in insert. 1451 * 1452 * Currently blob replace not supported 1453 * 1454 * returns 0 = fail, 1 = update, 2 = insert 1455 */ 1456 1457 function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) 1458 { 1459 global $ADODB_INCLUDED_LIB; 1460 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 1461 1462 return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc); 1463 } 1464 1465 1466 /** 1467 * Will select, getting rows from $offset (1-based), for $nrows. 1468 * This simulates the MySQL "select * from table limit $offset,$nrows" , and 1469 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that 1470 * MySQL and PostgreSQL parameter ordering is the opposite of the other. 1471 * eg. 1472 * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based) 1473 * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based) 1474 * 1475 * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set 1476 * 1477 * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional 1478 * @param sql 1479 * @param [offset] is the row to start calculations from (1-based) 1480 * @param [nrows] is the number of rows to get 1481 * @param [inputarr] array of bind variables 1482 * @return the recordset ($rs->databaseType == 'array') 1483 */ 1484 function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) 1485 { 1486 if (!is_numeric($secs2cache)) { 1487 if ($sql === false) $sql = -1; 1488 if ($offset == -1) $offset = false; 1489 // sql, nrows, offset,inputarr 1490 $rs =& $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs); 1491 } else { 1492 if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()"); 1493 $rs =& $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); 1494 } 1495 return $rs; 1496 } 1497 1498 /** 1499 * Flush cached recordsets that match a particular $sql statement. 1500 * If $sql == false, then we purge all files in the cache. 1501 */ 1502 function CacheFlush($sql=false,$inputarr=false) 1503 { 1504 global $ADODB_CACHE_DIR; 1505 1506 if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) { 1507 if (strncmp(PHP_OS,'WIN',3) === 0) { 1508 $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache'; 1509 } else { 1510 //$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f'; 1511 $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/'; 1512 // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`'; 1513 } 1514 if ($this->debug) { 1515 ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>"); 1516 } else { 1517 exec($cmd); 1518 } 1519 return; 1520 } 1521 1522 global $ADODB_INCLUDED_CSV; 1523 if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); 1524 1525 $f = $this->_gencachename($sql.serialize($inputarr),false); 1526 adodb_write_file($f,''); // is adodb_write_file needed? 1527 if (!@unlink($f)) { 1528 if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f"); 1529 } 1530 } 1531 1532 /** 1533 * Private function to generate filename for caching. 1534 * Filename is generated based on: 1535 * 1536 * - sql statement 1537 * - database type (oci8, ibase, ifx, etc) 1538 * - database name 1539 * - userid 1540 * - setFetchMode (adodb 4.23) 1541 * 1542 * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). 1543 * Assuming that we can have 50,000 files per directory with good performance, 1544 * then we can scale to 12.8 million unique cached recordsets. Wow! 1545 */ 1546 function _gencachename($sql,$createdir) 1547 { 1548 global $ADODB_CACHE_DIR; 1549 static $notSafeMode; 1550 1551 if ($this->fetchMode === false) { 1552 global $ADODB_FETCH_MODE; 1553 $mode = $ADODB_FETCH_MODE; 1554 } else { 1555 $mode = $this->fetchMode; 1556 } 1557 $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode); 1558 1559 if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode'); 1560 $dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR; 1561 1562 if ($createdir && $notSafeMode && !file_exists($dir)) { 1563 $oldu = umask(0); 1564 if (!mkdir($dir,0771)) 1565 if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql"); 1566 umask($oldu); 1567 } 1568 return $dir.'/adodb_'.$m.'.cache'; 1569 } 1570 1571 1572 /** 1573 * Execute SQL, caching recordsets. 1574 * 1575 * @param [secs2cache] seconds to cache data, set to 0 to force query. 1576 * This is an optional parameter. 1577 * @param sql SQL statement to execute 1578 * @param [inputarr] holds the input data to bind to 1579 * @return RecordSet or false 1580 */ 1581 function &CacheExecute($secs2cache,$sql=false,$inputarr=false) 1582 { 1583 1584 1585 if (!is_numeric($secs2cache)) { 1586 $inputarr = $sql; 1587 $sql = $secs2cache; 1588 $secs2cache = $this->cacheSecs; 1589 } 1590 1591 if (is_array($sql)) { 1592 $sqlparam = $sql; 1593 $sql = $sql[0]; 1594 } else 1595 $sqlparam = $sql; 1596 1597 global $ADODB_INCLUDED_CSV; 1598 if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); 1599 1600 $md5file = $this->_gencachename($sql.serialize($inputarr),true); 1601 $err = ''; 1602 1603 if ($secs2cache > 0){ 1604 $rs = &csv2rs($md5file,$err,$secs2cache,$this->arrayClass); 1605 $this->numCacheHits += 1; 1606 } else { 1607 $err='Timeout 1'; 1608 $rs = false; 1609 $this->numCacheMisses += 1; 1610 } 1611 if (!$rs) { 1612 // no cached rs found 1613 if ($this->debug) { 1614 if (get_magic_quotes_runtime()) { 1615 ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :("); 1616 } 1617 if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)"); 1618 } 1619 1620 $rs = &$this->Execute($sqlparam,$inputarr); 1621 1622 if ($rs) { 1623 $eof = $rs->EOF; 1624 $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately 1625 $txt = _rs2serialize($rs,false,$sql); // serialize 1626 1627 if (!adodb_write_file($md5file,$txt,$this->debug)) { 1628 if ($fn = $this->raiseErrorFn) { 1629 $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this); 1630 } 1631 if ($this->debug) ADOConnection::outp( " Cache write error"); 1632 } 1633 if ($rs->EOF && !$eof) { 1634 $rs->MoveFirst(); 1635 //$rs = &csv2rs($md5file,$err); 1636 $rs->connection = &$this; // Pablo suggestion 1637 } 1638 1639 } else 1640 @unlink($md5file); 1641 } else { 1642 $this->_errorMsg = ''; 1643 $this->_errorCode = 0; 1644 1645 if ($this->fnCacheExecute) { 1646 $fn = $this->fnCacheExecute; 1647 $fn($this, $secs2cache, $sql, $inputarr); 1648 } 1649 // ok, set cached object found 1650 $rs->connection = &$this; // Pablo suggestion 1651 if ($this->debug){ 1652 1653 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']); 1654 $ttl = $rs->timeCreated + $secs2cache - time(); 1655 $s = is_array($sql) ? $sql[0] : $sql; 1656 if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>'; 1657 1658 ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]"); 1659 } 1660 } 1661 return $rs; 1662 } 1663 1664 1665 /* 1666 Similar to PEAR DB's autoExecute(), except that 1667 $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE 1668 If $mode == 'UPDATE', then $where is compulsory as a safety measure. 1669 1670 $forceUpdate means that even if the data has not changed, perform update. 1671 */ 1672 function& AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false) 1673 { 1674 $sql = 'SELECT * FROM '.$table; 1675 if ($where!==FALSE) $sql .= ' WHERE '.$where; 1676 else if ($mode == 'UPDATE') { 1677 ADOConnection::outp('AutoExecute: Illegal mode=UPDATE with empty WHERE clause'); 1678 return false; 1679 } 1680 1681 $rs =& $this->SelectLimit($sql,1); 1682 if (!$rs) return false; // table does not exist 1683 $rs->tableName = $table; 1684 1685 switch((string) $mode) { 1686 case 'UPDATE': 1687 case '2': 1688 $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq); 1689 break; 1690 case 'INSERT': 1691 case '1': 1692 $sql = $this->GetInsertSQL($rs, $fields_values, $magicq); 1693 break; 1694 default: 1695 ADOConnection::outp("AutoExecute: Unknown mode=$mode"); 1696 return false; 1697 } 1698 $ret = false; 1699 if ($sql) $ret = $this->Execute($sql); 1700 if ($ret) $ret = true; 1701 return $ret; 1702 } 1703 1704 1705 /** 1706 * Generates an Update Query based on an existing recordset. 1707 * $arrFields is an associative array of fields with the value 1708 * that should be assigned. 1709 * 1710 * Note: This function should only be used on a recordset 1711 * that is run against a single table and sql should only 1712 * be a simple select stmt with no groupby/orderby/limit 1713 * 1714 * "Jonathan Younger" <jyounger@unilab.com> 1715 */ 1716 function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null) 1717 { 1718 global $ADODB_INCLUDED_LIB; 1719 1720 //********************************************************// 1721 //This is here to maintain compatibility 1722 //with older adodb versions. Sets force type to force nulls if $forcenulls is set. 1723 if (!isset($force)) { 1724 global $ADODB_FORCE_TYPE; 1725 $force = $ADODB_FORCE_TYPE; 1726 } 1727 //********************************************************// 1728 1729 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 1730 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force); 1731 } 1732 1733 1734 1735 1736 /** 1737 * Generates an Insert Query based on an existing recordset. 1738 * $arrFields is an associative array of fields with the value 1739 * that should be assigned. 1740 * 1741 * Note: This function should only be used on a recordset 1742 * that is run against a single table. 1743 */ 1744 function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null) 1745 { 1746 global $ADODB_INCLUDED_LIB; 1747 if (!isset($force)) { 1748 global $ADODB_FORCE_TYPE; 1749 $force = $ADODB_FORCE_TYPE; 1750 1751 } 1752 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 1753 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force); 1754 } 1755 1756 1757 /** 1758 * Update a blob column, given a where clause. There are more sophisticated 1759 * blob handling functions that we could have implemented, but all require 1760 * a very complex API. Instead we have chosen something that is extremely 1761 * simple to understand and use. 1762 * 1763 * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course. 1764 * 1765 * Usage to update a $blobvalue which has a primary key blob_id=1 into a 1766 * field blobtable.blobcolumn: 1767 * 1768 * UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1'); 1769 * 1770 * Insert example: 1771 * 1772 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); 1773 * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); 1774 */ 1775 1776 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') 1777 { 1778 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; 1779 } 1780 1781 /** 1782 * Usage: 1783 * UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1'); 1784 * 1785 * $blobtype supports 'BLOB' and 'CLOB' 1786 * 1787 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); 1788 * $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1'); 1789 */ 1790 function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') 1791 { 1792 $fd = fopen($path,'rb'); 1793 if ($fd === false) return false; 1794 $val = fread($fd,filesize($path)); 1795 fclose($fd); 1796 return $this->UpdateBlob($table,$column,$val,$where,$blobtype); 1797 } 1798 1799 function BlobDecode($blob) 1800 { 1801 return $blob; 1802 } 1803 1804 function BlobEncode($blob) 1805 { 1806 return $blob; 1807 } 1808 1809 function SetCharSet($charset) 1810 { 1811 return false; 1812 } 1813 1814 function IfNull( $field, $ifNull ) 1815 { 1816 return " CASE WHEN $field is null THEN $ifNull ELSE $field END "; 1817 } 1818 1819 function LogSQL($enable=true) 1820 { 1821 include_once(ADODB_DIR.'/adodb-perf.inc.php'); 1822 1823 if ($enable) $this->fnExecute = 'adodb_log_sql'; 1824 else $this->fnExecute = false; 1825 1826 $old = $this->_logsql; 1827 $this->_logsql = $enable; 1828 if ($enable && !$old) $this->_affected = false; 1829 return $old; 1830 } 1831 1832 function GetCharSet() 1833 { 1834 return false; 1835 } 1836 1837 /** 1838 * Usage: 1839 * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB'); 1840 * 1841 * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)'); 1842 * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1'); 1843 */ 1844 function UpdateClob($table,$column,$val,$where) 1845 { 1846 return $this->UpdateBlob($table,$column,$val,$where,'CLOB'); 1847 } 1848 1849 // not the fastest implementation - quick and dirty - jlim 1850 // for best performance, use the actual $rs->MetaType(). 1851 function MetaType($t,$len=-1,$fieldobj=false) 1852 { 1853 1854 if (empty($this->_metars)) { 1855 $rsclass = $this->rsPrefix.$this->databaseType; 1856 $this->_metars =& new $rsclass(false,$this->fetchMode); 1857 } 1858 1859 return $this->_metars->MetaType($t,$len,$fieldobj); 1860 } 1861 1862 1863 /** 1864 * Change the SQL connection locale to a specified locale. 1865 * This is used to get the date formats written depending on the client locale. 1866 */ 1867 function SetDateLocale($locale = 'En') 1868 { 1869 $this->locale = $locale; 1870 switch (strtoupper($locale)) 1871 { 1872 case 'EN': 1873 $this->fmtDate="'Y-m-d'"; 1874 $this->fmtTimeStamp = "'Y-m-d H:i:s'"; 1875 break; 1876 1877 case 'US': 1878 $this->fmtDate = "'m-d-Y'"; 1879 $this->fmtTimeStamp = "'m-d-Y H:i:s'"; 1880 break; 1881 1882 case 'NL': 1883 case 'FR': 1884 case 'RO': 1885 case 'IT': 1886 $this->fmtDate="'d-m-Y'"; 1887 $this->fmtTimeStamp = "'d-m-Y H:i:s'"; 1888 break; 1889 1890 case 'GE': 1891 $this->fmtDate="'d.m.Y'"; 1892 $this->fmtTimeStamp = "'d.m.Y H:i:s'"; 1893 break; 1894 1895 default: 1896 $this->fmtDate="'Y-m-d'"; 1897 $this->fmtTimeStamp = "'Y-m-d H:i:s'"; 1898 break; 1899 } 1900 } 1901 1902 1903 /** 1904 * Close Connection 1905 */ 1906 function Close() 1907 { 1908 $rez = $this->_close(); 1909 $this->_connectionID = false; 1910 return $rez; 1911 } 1912 1913 /** 1914 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans(). 1915 * 1916 * @return true if succeeded or false if database does not support transactions 1917 */ 1918 function BeginTrans() {return false;} 1919 1920 1921 /** 1922 * If database does not support transactions, always return true as data always commited 1923 * 1924 * @param $ok set to false to rollback transaction, true to commit 1925 * 1926 * @return true/false. 1927 */ 1928 function CommitTrans($ok=true) 1929 { return true;} 1930 1931 1932 /** 1933 * If database does not support transactions, rollbacks always fail, so return false 1934 * 1935 * @return true/false. 1936 */ 1937 function RollbackTrans() 1938 { return false;} 1939 1940 1941 /** 1942 * return the databases that the driver can connect to. 1943 * Some databases will return an empty array. 1944 * 1945 * @return an array of database names. 1946 */ 1947 function MetaDatabases() 1948 { 1949 global $ADODB_FETCH_MODE; 1950 1951 if ($this->metaDatabasesSQL) { 1952 $save = $ADODB_FETCH_MODE; 1953 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 1954 1955 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); 1956 1957 $arr = $this->GetCol($this->metaDatabasesSQL); 1958 if (isset($savem)) $this->SetFetchMode($savem); 1959 $ADODB_FETCH_MODE = $save; 1960 1961 return $arr; 1962 } 1963 1964 return false; 1965 } 1966 1967 /** 1968 * @param ttype can either be 'VIEW' or 'TABLE' or false. 1969 * If false, both views and tables are returned. 1970 * "VIEW" returns only views 1971 * "TABLE" returns only tables 1972 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE 1973 * @param mask is the input mask - only supported by oci8 and postgresql 1974 * 1975 * @return array of tables for current database. 1976 */ 1977 function &MetaTables($ttype=false,$showSchema=false,$mask=false) 1978 { 1979 global $ADODB_FETCH_MODE; 1980 1981 1982 $false = false; 1983 if ($mask) { 1984 return $false; 1985 } 1986 if ($this->metaTablesSQL) { 1987 $save = $ADODB_FETCH_MODE; 1988 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 1989 1990 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); 1991 1992 $rs = $this->Execute($this->metaTablesSQL); 1993 if (isset($savem)) $this->SetFetchMode($savem); 1994 $ADODB_FETCH_MODE = $save; 1995 1996 if ($rs === false) return $false; 1997 $arr =& $rs->GetArray(); 1998 $arr2 = array(); 1999 2000 if ($hast = ($ttype && isset($arr[0][1]))) { 2001 $showt = strncmp($ttype,'T',1); 2002 } 2003 2004 for ($i=0; $i < sizeof($arr); $i++) { 2005 if ($hast) { 2006 if ($showt == 0) { 2007 if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); 2008 } else { 2009 if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); 2010 } 2011 } else 2012 $arr2[] = trim($arr[$i][0]); 2013 } 2014 $rs->Close(); 2015 return $arr2; 2016 } 2017 return $false; 2018 } 2019 2020 2021 function _findschema(&$table,&$schema) 2022 { 2023 if (!$schema && ($at = strpos($table,'.')) !== false) { 2024 $schema = substr($table,0,$at); 2025 $table = substr($table,$at+1); 2026 } 2027 } 2028 2029 /** 2030 * List columns in a database as an array of ADOFieldObjects. 2031 * See top of file for definition of object. 2032 * 2033 * @param table table name to query 2034 * @param upper uppercase table name (required by some databases) 2035 * @schema is optional database schema to use - not supported by all databases. 2036 * 2037 * @return array of ADOFieldObjects for current table. 2038 */ 2039 function &MetaColumns($table,$upper=true) 2040 { 2041 global $ADODB_FETCH_MODE; 2042 2043 $false = false; 2044 2045 if (!empty($this->metaColumnsSQL)) { 2046 2047 $schema = false; 2048 $this->_findschema($table,$schema); 2049 2050 $save = $ADODB_FETCH_MODE; 2051 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 2052 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); 2053 $rs = $this->Execute(sprintf($this->metaColumnsSQL,($upper)?strtoupper($table):$table)); 2054 if (isset($savem)) $this->SetFetchMode($savem); 2055 $ADODB_FETCH_MODE = $save; 2056 if ($rs === false || $rs->EOF) return $false; 2057 2058 $retarr = array(); 2059 while (!$rs->EOF) { //print_r($rs->fields); 2060 $fld = new ADOFieldObject(); 2061 $fld->name = $rs->fields[0]; 2062 $fld->type = $rs->fields[1]; 2063 if (isset($rs->fields[3]) && $rs->fields[3]) { 2064 if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3]; 2065 $fld->scale = $rs->fields[4]; 2066 if ($fld->scale>0) $fld->max_length += 1; 2067 } else 2068 $fld->max_length = $rs->fields[2]; 2069 2070 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; 2071 else $retarr[strtoupper($fld->name)] = $fld; 2072 $rs->MoveNext(); 2073 } 2074 $rs->Close(); 2075 return $retarr; 2076 } 2077 return $false; 2078 } 2079 2080 /** 2081 * List indexes on a table as an array. 2082 * @param table table name to query 2083 * @param primary true to only show primary keys. Not actually used for most databases 2084 * 2085 * @return array of indexes on current table. Each element represents an index, and is itself an associative array. 2086 2087 Array ( 2088 [name_of_index] => Array 2089 ( 2090 [unique] => true or false 2091 [columns] => Array 2092 ( 2093 [0] => firstname 2094 [1] => lastname 2095 ) 2096 ) 2097 */ 2098 function &MetaIndexes($table, $primary = false, $owner = false) 2099 { 2100 $false = false; 2101 return $false; 2102 } 2103 2104 /** 2105 * List columns names in a table as an array. 2106 * @param table table name to query 2107 * 2108 * @return array of column names for current table. 2109 */ 2110 function &MetaColumnNames($table, $numIndexes=false) 2111 { 2112 $objarr =& $this->MetaColumns($table); 2113 if (!is_array($objarr)) { 2114 $false = false; 2115 return $false; 2116 } 2117 $arr = array(); 2118 if ($numIndexes) { 2119 $i = 0; 2120 foreach($objarr as $v) $arr[$i++] = $v->name; 2121 } else 2122 foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; 2123 2124 return $arr; 2125 } 2126 2127 /** 2128 * Different SQL databases used different methods to combine strings together. 2129 * This function provides a wrapper. 2130 * 2131 * param s variable number of string parameters 2132 * 2133 * Usage: $db->Concat($str1,$str2); 2134 * 2135 * @return concatenated string 2136 */ 2137 function Concat() 2138 { 2139 $arr = func_get_args(); 2140 return implode($this->concat_operator, $arr); 2141 } 2142 2143 2144 /** 2145 * Converts a date "d" to a string that the database can understand. 2146 * 2147 * @param d a date in Unix date time format. 2148 * 2149 * @return date string in database date format 2150 */ 2151 function DBDate($d) 2152 { 2153 if (empty($d) && $d !== 0) return 'null'; 2154 2155 if (is_string($d) && !is_numeric($d)) { 2156 if ($d === 'null' || strncmp($d,"'",1) === 0) return $d; 2157 if ($this->isoDates) return "'$d'"; 2158 $d = ADOConnection::UnixDate($d); 2159 } 2160 2161 return adodb_date($this->fmtDate,$d); 2162 } 2163 2164 2165 /** 2166 * Converts a timestamp "ts" to a string that the database can understand. 2167 * 2168 * @param ts a timestamp in Unix date time format. 2169 * 2170 * @return timestamp string in database timestamp format 2171 */ 2172 function DBTimeStamp($ts) 2173 { 2174 if (empty($ts) && $ts !== 0) return 'null'; 2175 2176 # strlen(14) allows YYYYMMDDHHMMSS format 2177 if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) 2178 return adodb_date($this->fmtTimeStamp,$ts); 2179 2180 if ($ts === 'null') return $ts; 2181 if ($this->isoDates && strlen($ts) !== 14) return "'$ts'"; 2182 2183 $ts = ADOConnection::UnixTimeStamp($ts); 2184 return adodb_date($this->fmtTimeStamp,$ts); 2185 } 2186 2187 /** 2188 * Also in ADORecordSet. 2189 * @param $v is a date string in YYYY-MM-DD format 2190 * 2191 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format 2192 */ 2193 function UnixDate($v) 2194 { 2195 if (is_object($v)) { 2196 // odbtp support 2197 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) 2198 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); 2199 } 2200 2201 if (is_numeric($v) && strlen($v) !== 8) return $v; 2202 if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", 2203 ($v), $rr)) return false; 2204 2205 if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0; 2206 // h-m-s-MM-DD-YY 2207 return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); 2208 } 2209 2210 2211 /** 2212 * Also in ADORecordSet. 2213 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format 2214 * 2215 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format 2216 */ 2217 function UnixTimeStamp($v) 2218 { 2219 if (is_object($v)) { 2220 // odbtp support 2221 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) 2222 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); 2223 } 2224 2225 if (!preg_match( 2226 "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", 2227 ($v), $rr)) return false; 2228 2229 if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0; 2230 2231 // h-m-s-MM-DD-YY 2232 if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); 2233 return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); 2234 } 2235 2236 /** 2237 * Also in ADORecordSet. 2238 * 2239 * Format database date based on user defined format. 2240 * 2241 * @param v is the character date in YYYY-MM-DD format, returned by database 2242 * @param fmt is the format to apply to it, using date() 2243 * 2244 * @return a date formated as user desires 2245 */ 2246 2247 function UserDate($v,$fmt='Y-m-d',$gmt=false) 2248 { 2249 $tt = $this->UnixDate($v); 2250 2251 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR 2252 if (($tt === false || $tt == -1) && $v != false) return $v; 2253 else if ($tt == 0) return $this->emptyDate; 2254 else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR 2255 } 2256 2257 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); 2258 2259 } 2260 2261 /** 2262 * 2263 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format 2264 * @param fmt is the format to apply to it, using date() 2265 * 2266 * @return a timestamp formated as user desires 2267 */ 2268 function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) 2269 { 2270 if (!isset($v)) return $this->emptyTimeStamp; 2271 # strlen(14) allows YYYYMMDDHHMMSS format 2272 if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); 2273 $tt = $this->UnixTimeStamp($v); 2274 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR 2275 if (($tt === false || $tt == -1) && $v != false) return $v; 2276 if ($tt == 0) return $this->emptyTimeStamp; 2277 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); 2278 } 2279 2280 function escape($s,$magic_quotes=false) 2281 { 2282 return $this->addq($s,$magic_quotes); 2283 } 2284 2285 /** 2286 * Quotes a string, without prefixing nor appending quotes. 2287 */ 2288 function addq($s,$magic_quotes=false) 2289 { 2290 if (!$magic_quotes) { 2291 2292 if ($this->replaceQuote[0] == '\\'){ 2293 // only since php 4.0.5 2294 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); 2295 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); 2296 } 2297 return str_replace("'",$this->replaceQuote,$s); 2298 } 2299 2300 // undo magic quotes for " 2301 $s = str_replace('\\"','"',$s); 2302 2303 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything 2304 return $s; 2305 else {// change \' to '' for sybase/mssql 2306 $s = str_replace('\\\\','\\',$s); 2307 return str_replace("\\'",$this->replaceQuote,$s); 2308 } 2309 } 2310 2311 /** 2312 * Correctly quotes a string so that all strings are escaped. We prefix and append 2313 * to the string single-quotes. 2314 * An example is $db->qstr("Don't bother",magic_quotes_runtime()); 2315 * 2316 * @param s the string to quote 2317 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). 2318 * This undoes the stupidity of magic quotes for GPC. 2319 * 2320 * @return quoted string to be sent back to database 2321 */ 2322 function qstr($s,$magic_quotes=false) 2323 { 2324 if (!$magic_quotes) { 2325 2326 if ($this->replaceQuote[0] == '\\'){ 2327 // only since php 4.0.5 2328 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); 2329 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); 2330 } 2331 return "'".str_replace("'",$this->replaceQuote,$s)."'"; 2332 } 2333 2334 // undo magic quotes for " 2335 $s = str_replace('\\"','"',$s); 2336 2337 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything 2338 return "'$s'"; 2339 else {// change \' to '' for sybase/mssql 2340 $s = str_replace('\\\\','\\',$s); 2341 return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; 2342 } 2343 } 2344 2345 2346 /** 2347 * Will select the supplied $page number from a recordset, given that it is paginated in pages of 2348 * $nrows rows per page. It also saves two boolean values saying if the given page is the first 2349 * and/or last one of the recordset. Added by Iv�n Oliva to provide recordset pagination. 2350 * 2351 * See readme.htm#ex8 for an example of usage. 2352 * 2353 * @param sql 2354 * @param nrows is the number of rows per page to get 2355 * @param page is the page number to get (1-based) 2356 * @param [inputarr] array of bind variables 2357 * @param [secs2cache] is a private parameter only used by jlim 2358 * @return the recordset ($rs->databaseType == 'array') 2359 * 2360 * NOTE: phpLens uses a different algorithm and does not use PageExecute(). 2361 * 2362 */ 2363 function &PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) 2364 { 2365 global $ADODB_INCLUDED_LIB; 2366 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 2367 if ($this->pageExecuteCountRows) $rs =& _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache); 2368 else $rs =& _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache); 2369 return $rs; 2370 } 2371 2372 2373 /** 2374 * Will select the supplied $page number from a recordset, given that it is paginated in pages of 2375 * $nrows rows per page. It also saves two boolean values saying if the given page is the first 2376 * and/or last one of the recordset. Added by Iv�n Oliva to provide recordset pagination. 2377 * 2378 * @param secs2cache seconds to cache data, set to 0 to force query 2379 * @param sql 2380 * @param nrows is the number of rows per page to get 2381 * @param page is the page number to get (1-based) 2382 * @param [inputarr] array of bind variables 2383 * @return the recordset ($rs->databaseType == 'array') 2384 */ 2385 function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) 2386 { 2387 /*switch($this->dataProvider) { 2388 case 'postgres': 2389 case 'mysql': 2390 break; 2391 default: $secs2cache = 0; break; 2392 }*/ 2393 $rs =& $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache); 2394 return $rs; 2395 } 2396 2397} // end class ADOConnection 2398 2399 2400 2401 //============================================================================================== 2402 // CLASS ADOFetchObj 2403 //============================================================================================== 2404 2405 /** 2406 * Internal placeholder for record objects. Used by ADORecordSet->FetchObj(). 2407 */ 2408 class ADOFetchObj { 2409 }; 2410 2411 //============================================================================================== 2412 // CLASS ADORecordSet_empty 2413 //============================================================================================== 2414 2415 /** 2416 * Lightweight recordset when there are no records to be returned 2417 */ 2418 class ADORecordSet_empty 2419 { 2420 var $dataProvider = 'empty'; 2421 var $databaseType = false; 2422 var $EOF = true; 2423 var $_numOfRows = 0; 2424 var $fields = false; 2425 var $connection = false; 2426 function RowCount() {return 0;} 2427 function RecordCount() {return 0;} 2428 function PO_RecordCount(){return 0;} 2429 function Close(){return true;} 2430 function FetchRow() {return false;} 2431 function FieldCount(){ return 0;} 2432 function Init() {} 2433 } 2434 2435 //============================================================================================== 2436 // DATE AND TIME FUNCTIONS 2437 //============================================================================================== 2438 include_once(ADODB_DIR.'/adodb-time.inc.php'); 2439 2440 //============================================================================================== 2441 // CLASS ADORecordSet 2442 //============================================================================================== 2443 2444 if (PHP_VERSION < 5) include_once(ADODB_DIR.'/adodb-php4.inc.php'); 2445 else include_once(ADODB_DIR.'/adodb-iterator.inc.php'); 2446 /** 2447 * RecordSet class that represents the dataset returned by the database. 2448 * To keep memory overhead low, this class holds only the current row in memory. 2449 * No prefetching of data is done, so the RecordCount() can return -1 ( which 2450 * means recordcount not known). 2451 */ 2452 class ADORecordSet extends ADODB_BASE_RS { 2453 /* 2454 * public variables 2455 */ 2456 var $dataProvider = "native"; 2457 var $fields = false; /// holds the current row data 2458 var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob 2459 /// in other words, we use a text area for editing. 2460 var $canSeek = false; /// indicates that seek is supported 2461 var $sql; /// sql text 2462 var $EOF = false; /// Indicates that the current record position is after the last record in a Recordset object. 2463 2464 var $emptyTimeStamp = ' '; /// what to display when $time==0 2465 var $emptyDate = ' '; /// what to display when $time==0 2466 var $debug = false; 2467 var $timeCreated=0; /// datetime in Unix format rs created -- for cached recordsets 2468 2469 var $bind = false; /// used by Fields() to hold array - should be private? 2470 var $fetchMode; /// default fetch mode 2471 var $connection = false; /// the parent connection 2472 /* 2473 * private variables 2474 */ 2475 var $_numOfRows = -1; /** number of rows, or -1 */ 2476 var $_numOfFields = -1; /** number of fields in recordset */ 2477 var $_queryID = -1; /** This variable keeps the result link identifier. */ 2478 var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */ 2479 var $_closed = false; /** has recordset been closed */ 2480 var $_inited = false; /** Init() should only be called once */ 2481 var $_obj; /** Used by FetchObj */ 2482 var $_names; /** Used by FetchObj */ 2483 2484 var $_currentPage = -1; /** Added by Iv�n Oliva to implement recordset pagination */ 2485 var $_atFirstPage = false; /** Added by Iv�n Oliva to implement recordset pagination */ 2486 var $_atLastPage = false; /** Added by Iv�n Oliva to implement recordset pagination */ 2487 var $_lastPageNo = -1; 2488 var $_maxRecordCount = 0; 2489 var $datetime = false; 2490 2491 /** 2492 * Constructor 2493 * 2494 * @param queryID this is the queryID returned by ADOConnection->_query() 2495 * 2496 */ 2497 function ADORecordSet($queryID) 2498 { 2499 $this->_queryID = $queryID; 2500 } 2501 2502 2503 2504 function Init() 2505 { 2506 if ($this->_inited) return; 2507 $this->_inited = true; 2508 if ($this->_queryID) @$this->_initrs(); 2509 else { 2510 $this->_numOfRows = 0; 2511 $this->_numOfFields = 0; 2512 } 2513 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) { 2514 2515 $this->_currentRow = 0; 2516 if ($this->EOF = ($this->_fetch() === false)) { 2517 $this->_numOfRows = 0; // _numOfRows could be -1 2518 } 2519 } else { 2520 $this->EOF = true; 2521 } 2522 } 2523 2524 2525 /** 2526 * Generate a SELECT tag string from a recordset, and return the string. 2527 * If the recordset has 2 cols, we treat the 1st col as the containing 2528 * the text to display to the user, and 2nd col as the return value. Default 2529 * strings are compared with the FIRST column. 2530 * 2531 * @param name name of SELECT tag 2532 * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox. 2533 * @param [blank1stItem] true to leave the 1st item in list empty 2534 * @param [multiple] true for listbox, false for popup 2535 * @param [size] #rows to show for listbox. not used by popup 2536 * @param [selectAttr] additional attributes to defined for SELECT tag. 2537 * useful for holding javascript onChange='...' handlers. 2538 & @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with 2539 * column 0 (1st col) if this is true. This is not documented. 2540 * 2541 * @return HTML 2542 * 2543 * changes by glen.davies@cce.ac.nz to support multiple hilited items 2544 */ 2545 function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false, 2546 $size=0, $selectAttr='',$compareFields0=true) 2547 { 2548 global $ADODB_INCLUDED_LIB; 2549 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 2550 return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple, 2551 $size, $selectAttr,$compareFields0); 2552 } 2553 2554 2555 2556 /** 2557 * Generate a SELECT tag string from a recordset, and return the string. 2558 * If the recordset has 2 cols, we treat the 1st col as the containing 2559 * the text to display to the user, and 2nd col as the return value. Default 2560 * strings are compared with the SECOND column. 2561 * 2562 */ 2563 function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='') 2564 { 2565 return $this->GetMenu($name,$defstr,$blank1stItem,$multiple, 2566 $size, $selectAttr,false); 2567 } 2568 2569 /* 2570 Grouped Menu 2571 */ 2572 function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false, 2573 $size=0, $selectAttr='') 2574 { 2575 global $ADODB_INCLUDED_LIB; 2576 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 2577 return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple, 2578 $size, $selectAttr,false); 2579 } 2580 2581 /** 2582 * return recordset as a 2-dimensional array. 2583 * 2584 * @param [nRows] is the number of rows to return. -1 means every row. 2585 * 2586 * @return an array indexed by the rows (0-based) from the recordset 2587 */ 2588 function &GetArray($nRows = -1) 2589 { 2590 global $ADODB_EXTENSION; if ($ADODB_EXTENSION) return adodb_getall($this,$nRows); 2591 2592 $results = array(); 2593 $cnt = 0; 2594 while (!$this->EOF && $nRows != $cnt) { 2595 $results[] = $this->fields; 2596 $this->MoveNext(); 2597 $cnt++; 2598 } 2599 return $results; 2600 } 2601 2602 function &GetAll($nRows = -1) 2603 { 2604 $arr =& $this->GetArray($nRows); 2605 return $arr; 2606 } 2607 2608 /* 2609 * Some databases allow multiple recordsets to be returned. This function 2610 * will return true if there is a next recordset, or false if no more. 2611 */ 2612 function NextRecordSet() 2613 { 2614 return false; 2615 } 2616 2617 /** 2618 * return recordset as a 2-dimensional array. 2619 * Helper function for ADOConnection->SelectLimit() 2620 * 2621 * @param offset is the row to start calculations from (1-based) 2622 * @param [nrows] is the number of rows to return 2623 * 2624 * @return an array indexed by the rows (0-based) from the recordset 2625 */ 2626 function &GetArrayLimit($nrows,$offset=-1) 2627 { 2628 if ($offset <= 0) { 2629 $arr =& $this->GetArray($nrows); 2630 return $arr; 2631 } 2632 2633 $this->Move($offset); 2634 2635 $results = array(); 2636 $cnt = 0; 2637 while (!$this->EOF && $nrows != $cnt) { 2638 $results[$cnt++] = $this->fields; 2639 $this->MoveNext(); 2640 } 2641 2642 return $results; 2643 } 2644 2645 2646 /** 2647 * Synonym for GetArray() for compatibility with ADO. 2648 * 2649 * @param [nRows] is the number of rows to return. -1 means every row. 2650 * 2651 * @return an array indexed by the rows (0-based) from the recordset 2652 */ 2653 function &GetRows($nRows = -1) 2654 { 2655 $arr =& $this->GetArray($nRows); 2656 return $arr; 2657 } 2658 2659 /** 2660 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. 2661 * The first column is treated as the key and is not included in the array. 2662 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless 2663 * $force_array == true. 2664 * 2665 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional 2666 * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing, 2667 * read the source. 2668 * 2669 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and 2670 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1 2671 * 2672 * @return an associative array indexed by the first column of the array, 2673 * or false if the data has less than 2 cols. 2674 */ 2675 function &GetAssoc($force_array = false, $first2cols = false) 2676 { 2677 global $ADODB_EXTENSION; 2678 2679 $cols = $this->_numOfFields; 2680 if ($cols < 2) { 2681 $false = false; 2682 return $false; 2683 } 2684 $numIndex = isset($this->fields[0]); 2685 $results = array(); 2686 2687 if (!$first2cols && ($cols > 2 || $force_array)) { 2688 if ($ADODB_EXTENSION) { 2689 if ($numIndex) { 2690 while (!$this->EOF) { 2691 $results[trim($this->fields[0])] = array_slice($this->fields, 1); 2692 adodb_movenext($this); 2693 } 2694 } else { 2695 while (!$this->EOF) { 2696 $results[trim(reset($this->fields))] = array_slice($this->fields, 1); 2697 adodb_movenext($this); 2698 } 2699 } 2700 } else { 2701 if ($numIndex) { 2702 while (!$this->EOF) { 2703 $results[trim($this->fields[0])] = array_slice($this->fields, 1); 2704 $this->MoveNext(); 2705 } 2706 } else { 2707 while (!$this->EOF) { 2708 $results[trim(reset($this->fields))] = array_slice($this->fields, 1); 2709 $this->MoveNext(); 2710 } 2711 } 2712 } 2713 } else { 2714 if ($ADODB_EXTENSION) { 2715 // return scalar values 2716 if ($numIndex) { 2717 while (!$this->EOF) { 2718 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string 2719 $results[trim(($this->fields[0]))] = $this->fields[1]; 2720 adodb_movenext($this); 2721 } 2722 } else { 2723 while (!$this->EOF) { 2724 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string 2725 $v1 = trim(reset($this->fields)); 2726 $v2 = ''.next($this->fields); 2727 $results[$v1] = $v2; 2728 adodb_movenext($this); 2729 } 2730 } 2731 } else { 2732 if ($numIndex) { 2733 while (!$this->EOF) { 2734 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string 2735 $results[trim(($this->fields[0]))] = $this->fields[1]; 2736 $this->MoveNext(); 2737 } 2738 } else { 2739 while (!$this->EOF) { 2740 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string 2741 $v1 = trim(reset($this->fields)); 2742 $v2 = ''.next($this->fields); 2743 $results[$v1] = $v2; 2744 $this->MoveNext(); 2745 } 2746 } 2747 } 2748 } 2749 return $results; 2750 } 2751 2752 2753 /** 2754 * 2755 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format 2756 * @param fmt is the format to apply to it, using date() 2757 * 2758 * @return a timestamp formated as user desires 2759 */ 2760 function UserTimeStamp($v,$fmt='Y-m-d H:i:s') 2761 { 2762 if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v); 2763 $tt = $this->UnixTimeStamp($v); 2764 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR 2765 if (($tt === false || $tt == -1) && $v != false) return $v; 2766 if ($tt === 0) return $this->emptyTimeStamp; 2767 return adodb_date($fmt,$tt); 2768 } 2769 2770 2771 /** 2772 * @param v is the character date in YYYY-MM-DD format, returned by database 2773 * @param fmt is the format to apply to it, using date() 2774 * 2775 * @return a date formated as user desires 2776 */ 2777 function UserDate($v,$fmt='Y-m-d') 2778 { 2779 $tt = $this->UnixDate($v); 2780 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR 2781 if (($tt === false || $tt == -1) && $v != false) return $v; 2782 else if ($tt == 0) return $this->emptyDate; 2783 else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR 2784 } 2785 return adodb_date($fmt,$tt); 2786 } 2787 2788 2789 /** 2790 * @param $v is a date string in YYYY-MM-DD format 2791 * 2792 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format 2793 */ 2794 function UnixDate($v) 2795 { 2796 return ADOConnection::UnixDate($v); 2797 } 2798 2799 2800 /** 2801 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format 2802 * 2803 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format 2804 */ 2805 function UnixTimeStamp($v) 2806 { 2807 return ADOConnection::UnixTimeStamp($v); 2808 } 2809 2810 2811 /** 2812 * PEAR DB Compat - do not use internally 2813 */ 2814 function Free() 2815 { 2816 return $this->Close(); 2817 } 2818 2819 2820 /** 2821 * PEAR DB compat, number of rows 2822 */ 2823 function NumRows() 2824 { 2825 return $this->_numOfRows; 2826 } 2827 2828 2829 /** 2830 * PEAR DB compat, number of cols 2831 */ 2832 function NumCols() 2833 { 2834 return $this->_numOfFields; 2835 } 2836 2837 /** 2838 * Fetch a row, returning false if no more rows. 2839 * This is PEAR DB compat mode. 2840 * 2841 * @return false or array containing the current record 2842 */ 2843 function &FetchRow() 2844 { 2845 if ($this->EOF) { 2846 $false = false; 2847 return $false; 2848 } 2849 $arr = $this->fields; 2850 $this->_currentRow++; 2851 if (!$this->_fetch()) $this->EOF = true; 2852 return $arr; 2853 } 2854 2855 2856 /** 2857 * Fetch a row, returning PEAR_Error if no more rows. 2858 * This is PEAR DB compat mode. 2859 * 2860 * @return DB_OK or error object 2861 */ 2862 function FetchInto(&$arr) 2863 { 2864 if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false; 2865 $arr = $this->fields; 2866 $this->MoveNext(); 2867 return 1; // DB_OK 2868 } 2869 2870 2871 /** 2872 * Move to the first row in the recordset. Many databases do NOT support this. 2873 * 2874 * @return true or false 2875 */ 2876 function MoveFirst() 2877 { 2878 if ($this->_currentRow == 0) return true; 2879 return $this->Move(0); 2880 } 2881 2882 2883 /** 2884 * Move to the last row in the recordset. 2885 * 2886 * @return true or false 2887 */ 2888 function MoveLast() 2889 { 2890 if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1); 2891 if ($this->EOF) return false; 2892 while (!$this->EOF) { 2893 $f = $this->fields; 2894 $this->MoveNext(); 2895 } 2896 $this->fields = $f; 2897 $this->EOF = false; 2898 return true; 2899 } 2900 2901 2902 /** 2903 * Move to next record in the recordset. 2904 * 2905 * @return true if there still rows available, or false if there are no more rows (EOF). 2906 */ 2907 function MoveNext() 2908 { 2909 if (!$this->EOF) { 2910 $this->_currentRow++; 2911 if ($this->_fetch()) return true; 2912 } 2913 $this->EOF = true; 2914 /* -- tested error handling when scrolling cursor -- seems useless. 2915 $conn = $this->connection; 2916 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) { 2917 $fn = $conn->raiseErrorFn; 2918 $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database); 2919 } 2920 */ 2921 return false; 2922 } 2923 2924 2925 /** 2926 * Random access to a specific row in the recordset. Some databases do not support 2927 * access to previous rows in the databases (no scrolling backwards). 2928 * 2929 * @param rowNumber is the row to move to (0-based) 2930 * 2931 * @return true if there still rows available, or false if there are no more rows (EOF). 2932 */ 2933 function Move($rowNumber = 0) 2934 { 2935 $this->EOF = false; 2936 if ($rowNumber == $this->_currentRow) return true; 2937 if ($rowNumber >= $this->_numOfRows) 2938 if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2; 2939 2940 if ($this->canSeek) { 2941 2942 if ($this->_seek($rowNumber)) { 2943 $this->_currentRow = $rowNumber; 2944 if ($this->_fetch()) { 2945 return true; 2946 } 2947 } else { 2948 $this->EOF = true; 2949 return false; 2950 } 2951 } else { 2952 if ($rowNumber < $this->_currentRow) return false; 2953 global $ADODB_EXTENSION; 2954 if ($ADODB_EXTENSION) { 2955 while (!$this->EOF && $this->_currentRow < $rowNumber) { 2956 adodb_movenext($this); 2957 } 2958 } else { 2959 2960 while (! $this->EOF && $this->_currentRow < $rowNumber) { 2961 $this->_currentRow++; 2962 2963 if (!$this->_fetch()) $this->EOF = true; 2964 } 2965 } 2966 return !($this->EOF); 2967 } 2968 2969 $this->fields = false; 2970 $this->EOF = true; 2971 return false; 2972 } 2973 2974 2975 /** 2976 * Get the value of a field in the current row by column name. 2977 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM. 2978 * 2979 * @param colname is the field to access 2980 * 2981 * @return the value of $colname column 2982 */ 2983 function Fields($colname) 2984 { 2985 return $this->fields[$colname]; 2986 } 2987 2988 function GetAssocKeys($upper=true) 2989 { 2990 $this->bind = array(); 2991 for ($i=0; $i < $this->_numOfFields; $i++) { 2992 $o = $this->FetchField($i); 2993 if ($upper === 2) $this->bind[$o->name] = $i; 2994 else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i; 2995 } 2996 } 2997 2998 /** 2999 * Use associative array to get fields array for databases that do not support 3000 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it 3001 * 3002 * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC 3003 * before you execute your SQL statement, and access $rs->fields['col'] directly. 3004 * 3005 * $upper 0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField 3006 */ 3007 function &GetRowAssoc($upper=1) 3008 { 3009 $record = array(); 3010 // if (!$this->fields) return $record; 3011 3012 if (!$this->bind) { 3013 $this->GetAssocKeys($upper); 3014 } 3015 3016 foreach($this->bind as $k => $v) { 3017 $record[$k] = $this->fields[$v]; 3018 } 3019 3020 return $record; 3021 } 3022 3023 3024 /** 3025 * Clean up recordset 3026 * 3027 * @return true or false 3028 */ 3029 function Close() 3030 { 3031 // free connection object - this seems to globally free the object 3032 // and not merely the reference, so don't do this... 3033 // $this->connection = false; 3034 if (!$this->_closed) { 3035 $this->_closed = true; 3036 return $this->_close(); 3037 } else 3038 return true; 3039 } 3040 3041 /** 3042 * synonyms RecordCount and RowCount 3043 * 3044 * @return the number of rows or -1 if this is not supported 3045 */ 3046 function RecordCount() {return $this->_numOfRows;} 3047 3048 3049 /* 3050 * If we are using PageExecute(), this will return the maximum possible rows 3051 * that can be returned when paging a recordset. 3052 */ 3053 function MaxRecordCount() 3054 { 3055 return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount(); 3056 } 3057 3058 /** 3059 * synonyms RecordCount and RowCount 3060 * 3061 * @return the number of rows or -1 if this is not supported 3062 */ 3063 function RowCount() {return $this->_numOfRows;} 3064 3065 3066 /** 3067 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org> 3068 * 3069 * @return the number of records from a previous SELECT. All databases support this. 3070 * 3071 * But aware possible problems in multiuser environments. For better speed the table 3072 * must be indexed by the condition. Heavy test this before deploying. 3073 */ 3074 function PO_RecordCount($table="", $condition="") { 3075 3076 $lnumrows = $this->_numOfRows; 3077 // the database doesn't support native recordcount, so we do a workaround 3078 if ($lnumrows == -1 && $this->connection) { 3079 IF ($table) { 3080 if ($condition) $condition = " WHERE " . $condition; 3081 $resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition"); 3082 if ($resultrows) $lnumrows = reset($resultrows->fields); 3083 } 3084 } 3085 return $lnumrows; 3086 } 3087 3088 /** 3089 * @return the current row in the recordset. If at EOF, will return the last row. 0-based. 3090 */ 3091 function CurrentRow() {return $this->_currentRow;} 3092 3093 /** 3094 * synonym for CurrentRow -- for ADO compat 3095 * 3096 * @return the current row in the recordset. If at EOF, will return the last row. 0-based. 3097 */ 3098 function AbsolutePosition() {return $this->_currentRow;} 3099 3100 /** 3101 * @return the number of columns in the recordset. Some databases will set this to 0 3102 * if no records are returned, others will return the number of columns in the query. 3103 */ 3104 function FieldCount() {return $this->_numOfFields;} 3105 3106 3107 /** 3108 * Get the ADOFieldObject of a specific column. 3109 * 3110 * @param fieldoffset is the column position to access(0-based). 3111 * 3112 * @return the ADOFieldObject for that column, or false. 3113 */ 3114 function &FetchField($fieldoffset) 3115 { 3116 // must be defined by child class 3117 } 3118 3119 /** 3120 * Get the ADOFieldObjects of all columns in an array. 3121 * 3122 */ 3123 function& FieldTypesArray() 3124 { 3125 $arr = array(); 3126 for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) 3127 $arr[] = $this->FetchField($i); 3128 return $arr; 3129 } 3130 3131 /** 3132 * Return the fields array of the current row as an object for convenience. 3133 * The default case is lowercase field names. 3134 * 3135 * @return the object with the properties set to the fields of the current row 3136 */ 3137 function &FetchObj() 3138 { 3139 $o =& $this->FetchObject(false); 3140 return $o; 3141 } 3142 3143 /** 3144 * Return the fields array of the current row as an object for convenience. 3145 * The default case is uppercase. 3146 * 3147 * @param $isupper to set the object property names to uppercase 3148 * 3149 * @return the object with the properties set to the fields of the current row 3150 */ 3151 function &FetchObject($isupper=true) 3152 { 3153 if (empty($this->_obj)) { 3154 $this->_obj = new ADOFetchObj(); 3155 $this->_names = array(); 3156 for ($i=0; $i <$this->_numOfFields; $i++) { 3157 $f = $this->FetchField($i); 3158 $this->_names[] = $f->name; 3159 } 3160 } 3161 $i = 0; 3162 if (PHP_VERSION >= 5) $o = clone($this->_obj); 3163 else $o = $this->_obj; 3164 3165 for ($i=0; $i <$this->_numOfFields; $i++) { 3166 $name = $this->_names[$i]; 3167 if ($isupper) $n = strtoupper($name); 3168 else $n = $name; 3169 3170 $o->$n = $this->Fields($name); 3171 } 3172 return $o; 3173 } 3174 3175 /** 3176 * Return the fields array of the current row as an object for convenience. 3177 * The default is lower-case field names. 3178 * 3179 * @return the object with the properties set to the fields of the current row, 3180 * or false if EOF 3181 * 3182 * Fixed bug reported by tim@orotech.net 3183 */ 3184 function &FetchNextObj() 3185 { 3186 $o =& $this->FetchNextObject(false); 3187 return $o; 3188 } 3189 3190 3191 /** 3192 * Return the fields array of the current row as an object for convenience. 3193 * The default is upper case field names. 3194 * 3195 * @param $isupper to set the object property names to uppercase 3196 * 3197 * @return the object with the properties set to the fields of the current row, 3198 * or false if EOF 3199 * 3200 * Fixed bug reported by tim@orotech.net 3201 */ 3202 function &FetchNextObject($isupper=true) 3203 { 3204 $o = false; 3205 if ($this->_numOfRows != 0 && !$this->EOF) { 3206 $o = $this->FetchObject($isupper); 3207 $this->_currentRow++; 3208 if ($this->_fetch()) return $o; 3209 } 3210 $this->EOF = true; 3211 return $o; 3212 } 3213 3214 /** 3215 * Get the metatype of the column. This is used for formatting. This is because 3216 * many databases use different names for the same type, so we transform the original 3217 * type to our standardised version which uses 1 character codes: 3218 * 3219 * @param t is the type passed in. Normally is ADOFieldObject->type. 3220 * @param len is the maximum length of that field. This is because we treat character 3221 * fields bigger than a certain size as a 'B' (blob). 3222 * @param fieldobj is the field object returned by the database driver. Can hold 3223 * additional info (eg. primary_key for mysql). 3224 * 3225 * @return the general type of the data: 3226 * C for character < 250 chars 3227 * X for teXt (>= 250 chars) 3228 * B for Binary 3229 * N for numeric or floating point 3230 * D for date 3231 * T for timestamp 3232 * L for logical/Boolean 3233 * I for integer 3234 * R for autoincrement counter/integer 3235 * 3236 * 3237 */ 3238 function MetaType($t,$len=-1,$fieldobj=false) 3239 { 3240 if (is_object($t)) { 3241 $fieldobj = $t; 3242 $t = $fieldobj->type; 3243 $len = $fieldobj->max_length; 3244 } 3245 // changed in 2.32 to hashing instead of switch stmt for speed... 3246 static $typeMap = array( 3247 'VARCHAR' => 'C', 3248 'VARCHAR2' => 'C', 3249 'CHAR' => 'C', 3250 'C' => 'C', 3251 'STRING' => 'C', 3252 'NCHAR' => 'C', 3253 'NVARCHAR' => 'C', 3254 'VARYING' => 'C', 3255 'BPCHAR' => 'C', 3256 'CHARACTER' => 'C', 3257 'INTERVAL' => 'C', # Postgres 3258 ## 3259 'LONGCHAR' => 'X', 3260 'TEXT' => 'X', 3261 'NTEXT' => 'X', 3262 'M' => 'X', 3263 'X' => 'X', 3264 'CLOB' => 'X', 3265 'NCLOB' => 'X', 3266 'LVARCHAR' => 'X', 3267 ## 3268 'BLOB' => 'B', 3269 'IMAGE' => 'B', 3270 'BINARY' => 'B', 3271 'VARBINARY' => 'B', 3272 'LONGBINARY' => 'B', 3273 'B' => 'B', 3274 ## 3275 'YEAR' => 'D', // mysql 3276 'DATE' => 'D', 3277 'D' => 'D', 3278 ## 3279 'TIME' => 'T', 3280 'TIMESTAMP' => 'T', 3281 'DATETIME' => 'T', 3282 'TIMESTAMPTZ' => 'T', 3283 'T' => 'T', 3284 ## 3285 'BOOL' => 'L', 3286 'BOOLEAN' => 'L', 3287 'BIT' => 'L', 3288 'L' => 'L', 3289 ## 3290 'COUNTER' => 'R', 3291 'R' => 'R', 3292 'SERIAL' => 'R', // ifx 3293 'INT IDENTITY' => 'R', 3294 ## 3295 'INT' => 'I', 3296 'INT2' => 'I', 3297 'INT4' => 'I', 3298 'INT8' => 'I', 3299 'INTEGER' => 'I', 3300 'INTEGER UNSIGNED' => 'I', 3301 'SHORT' => 'I', 3302 'TINYINT' => 'I', 3303 'SMALLINT' => 'I', 3304 'I' => 'I', 3305 ## 3306 'LONG' => 'N', // interbase is numeric, oci8 is blob 3307 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers 3308 'DECIMAL' => 'N', 3309 'DEC' => 'N', 3310 'REAL' => 'N', 3311 'DOUBLE' => 'N', 3312 'DOUBLE PRECISION' => 'N', 3313 'SMALLFLOAT' => 'N', 3314 'FLOAT' => 'N', 3315 'NUMBER' => 'N', 3316 'NUM' => 'N', 3317 'NUMERIC' => 'N', 3318 'MONEY' => 'N', 3319 3320 ## informix 9.2 3321 'SQLINT' => 'I', 3322 'SQLSERIAL' => 'I', 3323 'SQLSMINT' => 'I', 3324 'SQLSMFLOAT' => 'N', 3325 'SQLFLOAT' => 'N', 3326 'SQLMONEY' => 'N', 3327 'SQLDECIMAL' => 'N', 3328 'SQLDATE' => 'D', 3329 'SQLVCHAR' => 'C', 3330 'SQLCHAR' => 'C', 3331 'SQLDTIME' => 'T', 3332 'SQLINTERVAL' => 'N', 3333 'SQLBYTES' => 'B', 3334 'SQLTEXT' => 'X' 3335 ); 3336 3337 $tmap = false; 3338 $t = strtoupper($t); 3339 $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; 3340 switch ($tmap) { 3341 case 'C': 3342 3343 // is the char field is too long, return as text field... 3344 if ($this->blobSize >= 0) { 3345 if ($len > $this->blobSize) return 'X'; 3346 } else if ($len > 250) { 3347 return 'X'; 3348 } 3349 return 'C'; 3350 3351 case 'I': 3352 if (!empty($fieldobj->primary_key)) return 'R'; 3353 return 'I'; 3354 3355 case false: 3356 return 'N'; 3357 3358 case 'B': 3359 if (isset($fieldobj->binary)) 3360 return ($fieldobj->binary) ? 'B' : 'X'; 3361 return 'B'; 3362 3363 case 'D': 3364 if (!empty($this->datetime)) return 'T'; 3365 return 'D'; 3366 3367 default: 3368 if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; 3369 return $tmap; 3370 } 3371 } 3372 3373 function _close() {} 3374 3375 /** 3376 * set/returns the current recordset page when paginating 3377 */ 3378 function AbsolutePage($page=-1) 3379 { 3380 if ($page != -1) $this->_currentPage = $page; 3381 return $this->_currentPage; 3382 } 3383 3384 /** 3385 * set/returns the status of the atFirstPage flag when paginating 3386 */ 3387 function AtFirstPage($status=false) 3388 { 3389 if ($status != false) $this->_atFirstPage = $status; 3390 return $this->_atFirstPage; 3391 } 3392 3393 function LastPageNo($page = false) 3394 { 3395 if ($page != false) $this->_lastPageNo = $page; 3396 return $this->_lastPageNo; 3397 } 3398 3399 /** 3400 * set/returns the status of the atLastPage flag when paginating 3401 */ 3402 function AtLastPage($status=false) 3403 { 3404 if ($status != false) $this->_atLastPage = $status; 3405 return $this->_atLastPage; 3406 } 3407 3408} // end class ADORecordSet 3409 3410 //============================================================================================== 3411 // CLASS ADORecordSet_array 3412 //============================================================================================== 3413 3414 /** 3415 * This class encapsulates the concept of a recordset created in memory 3416 * as an array. This is useful for the creation of cached recordsets. 3417 * 3418 * Note that the constructor is different from the standard ADORecordSet 3419 */ 3420 3421 class ADORecordSet_array extends ADORecordSet 3422 { 3423 var $databaseType = 'array'; 3424 3425 var $_array; // holds the 2-dimensional data array 3426 var $_types; // the array of types of each column (C B I L M) 3427 var $_colnames; // names of each column in array 3428 var $_skiprow1; // skip 1st row because it holds column names 3429 var $_fieldarr; // holds array of field objects 3430 var $canSeek = true; 3431 var $affectedrows = false; 3432 var $insertid = false; 3433 var $sql = ''; 3434 var $compat = false; 3435 /** 3436 * Constructor 3437 * 3438 */ 3439 function ADORecordSet_array($fakeid=1) 3440 { 3441 global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH; 3442 3443 // fetch() on EOF does not delete $this->fields 3444 $this->compat = !empty($ADODB_COMPAT_FETCH); 3445 $this->ADORecordSet($fakeid); // fake queryID 3446 $this->fetchMode = $ADODB_FETCH_MODE; 3447 } 3448 3449 3450 /** 3451 * Setup the array. 3452 * 3453 * @param array is a 2-dimensional array holding the data. 3454 * The first row should hold the column names 3455 * unless paramter $colnames is used. 3456 * @param typearr holds an array of types. These are the same types 3457 * used in MetaTypes (C,B,L,I,N). 3458 * @param [colnames] array of column names. If set, then the first row of 3459 * $array should not hold the column names. 3460 */ 3461 function InitArray($array,$typearr,$colnames=false) 3462 { 3463 $this->_array = $array; 3464 $this->_types = $typearr; 3465 if ($colnames) { 3466 $this->_skiprow1 = false; 3467 $this->_colnames = $colnames; 3468 } else { 3469 $this->_skiprow1 = true; 3470 $this->_colnames = $array[0]; 3471 } 3472 $this->Init(); 3473 } 3474 /** 3475 * Setup the Array and datatype file objects 3476 * 3477 * @param array is a 2-dimensional array holding the data. 3478 * The first row should hold the column names 3479 * unless paramter $colnames is used. 3480 * @param fieldarr holds an array of ADOFieldObject's. 3481 */ 3482 function InitArrayFields(&$array,&$fieldarr) 3483 { 3484 $this->_array =& $array; 3485 $this->_skiprow1= false; 3486 if ($fieldarr) { 3487 $this->_fieldobjects =& $fieldarr; 3488 } 3489 $this->Init(); 3490 } 3491 3492 function &GetArray($nRows=-1) 3493 { 3494 if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) { 3495 return $this->_array; 3496 } else { 3497 $arr =& ADORecordSet::GetArray($nRows); 3498 return $arr; 3499 } 3500 } 3501 3502 function _initrs() 3503 { 3504 $this->_numOfRows = sizeof($this->_array); 3505 if ($this->_skiprow1) $this->_numOfRows -= 1; 3506 3507 $this->_numOfFields =(isset($this->_fieldobjects)) ? 3508 sizeof($this->_fieldobjects):sizeof($this->_types); 3509 } 3510 3511 /* Use associative array to get fields array */ 3512 function Fields($colname) 3513 { 3514 $mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode; 3515 3516 if ($mode & ADODB_FETCH_ASSOC) { 3517 if (!isset($this->fields[$colname])) $colname = strtolower($colname); 3518 return $this->fields[$colname]; 3519 } 3520 if (!$this->bind) { 3521 $this->bind = array(); 3522 for ($i=0; $i < $this->_numOfFields; $i++) { 3523 $o = $this->FetchField($i); 3524 $this->bind[strtoupper($o->name)] = $i; 3525 } 3526 } 3527 return $this->fields[$this->bind[strtoupper($colname)]]; 3528 } 3529 3530 function &FetchField($fieldOffset = -1) 3531 { 3532 if (isset($this->_fieldobjects)) { 3533 return $this->_fieldobjects[$fieldOffset]; 3534 } 3535 $o = new ADOFieldObject(); 3536 $o->name = $this->_colnames[$fieldOffset]; 3537 $o->type = $this->_types[$fieldOffset]; 3538 $o->max_length = -1; // length not known 3539 3540 return $o; 3541 } 3542 3543 function _seek($row) 3544 { 3545 if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) { 3546 $this->_currentRow = $row; 3547 if ($this->_skiprow1) $row += 1; 3548 $this->fields = $this->_array[$row]; 3549 return true; 3550 } 3551 return false; 3552 } 3553 3554 function MoveNext() 3555 { 3556 if (!$this->EOF) { 3557 $this->_currentRow++; 3558 3559 $pos = $this->_currentRow; 3560 3561 if ($this->_numOfRows <= $pos) { 3562 if (!$this->compat) $this->fields = false; 3563 } else { 3564 if ($this->_skiprow1) $pos += 1; 3565 $this->fields = $this->_array[$pos]; 3566 return true; 3567 } 3568 $this->EOF = true; 3569 } 3570 3571 return false; 3572 } 3573 3574 function _fetch() 3575 { 3576 $pos = $this->_currentRow; 3577 3578 if ($this->_numOfRows <= $pos) { 3579 if (!$this->compat) $this->fields = false; 3580 return false; 3581 } 3582 if ($this->_skiprow1) $pos += 1; 3583 $this->fields = $this->_array[$pos]; 3584 return true; 3585 } 3586 3587 function _close() 3588 { 3589 return true; 3590 } 3591 3592 } // ADORecordSet_array 3593 3594 //============================================================================================== 3595 // HELPER FUNCTIONS 3596 //============================================================================================== 3597 3598 /** 3599 * Synonym for ADOLoadCode. Private function. Do not use. 3600 * 3601 * @deprecated 3602 */ 3603 function ADOLoadDB($dbType) 3604 { 3605 return ADOLoadCode($dbType); 3606 } 3607 3608 /** 3609 * Load the code for a specific database driver. Private function. Do not use. 3610 */ 3611 function ADOLoadCode($dbType) 3612 { 3613 global $ADODB_LASTDB; 3614 3615 if (!$dbType) return false; 3616 $db = strtolower($dbType); 3617 switch ($db) { 3618 case 'ado': 3619 if (PHP_VERSION >= 5) $db = 'ado5'; 3620 $class = 'ado'; 3621 break; 3622 case 'ifx': 3623 case 'maxsql': $class = $db = 'mysqlt'; break; 3624 case 'postgres': 3625 case 'postgres8': 3626 case 'pgsql': $class = $db = 'postgres7'; break; 3627 default: 3628 $class = $db; break; 3629 } 3630 3631 $file = ADODB_DIR."/drivers/adodb-".$db.".inc.php"; 3632 @include_once($file); 3633 $ADODB_LASTDB = $class; 3634 if (class_exists("ADODB_" . $class)) return $class; 3635 3636 //ADOConnection::outp(adodb_pr(get_declared_classes(),true)); 3637 if (!file_exists($file)) ADOConnection::outp("Missing file: $file"); 3638 else ADOConnection::outp("Syntax error in file: $file"); 3639 return false; 3640 } 3641 3642 /** 3643 * synonym for ADONewConnection for people like me who cannot remember the correct name 3644 */ 3645 function &NewADOConnection($db='') 3646 { 3647 $tmp =& ADONewConnection($db); 3648 return $tmp; 3649 } 3650 3651 /** 3652 * Instantiate a new Connection class for a specific database driver. 3653 * 3654 * @param [db] is the database Connection object to create. If undefined, 3655 * use the last database driver that was loaded by ADOLoadCode(). 3656 * 3657 * @return the freshly created instance of the Connection class. 3658 */ 3659 function &ADONewConnection($db='') 3660 { 3661 GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB; 3662 3663 if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); 3664 $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false; 3665 $false = false; 3666 if (strpos($db,'://')) { 3667 $origdsn = $db; 3668 $dsna = @parse_url($db); 3669 3670 if (!$dsna) { 3671 // special handling of oracle, which might not have host 3672 $db = str_replace('@/','@adodb-fakehost/',$db); 3673 $dsna = parse_url($db); 3674 if (!$dsna) return $false; 3675 $dsna['host'] = ''; 3676 } 3677 $db = @$dsna['scheme']; 3678 if (!$db) return $false; 3679 $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : ''; 3680 $dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : ''; 3681 $dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : ''; 3682 $dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial / 3683 3684 if (isset($dsna['query'])) { 3685 $opt1 = explode('&',$dsna['query']); 3686 foreach($opt1 as $k => $v) { 3687 $arr = explode('=',$v); 3688 $opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1; 3689 } 3690 } else $opt = array(); 3691 } 3692 3693 /* 3694 * phptype: Database backend used in PHP (mysql, odbc etc.) 3695 * dbsyntax: Database used with regards to SQL syntax etc. 3696 * protocol: Communication protocol to use (tcp, unix etc.) 3697 * hostspec: Host specification (hostname[:port]) 3698 * database: Database to use on the DBMS server 3699 * username: User name for login 3700 * password: Password for login 3701 */ 3702 if (!empty($ADODB_NEWCONNECTION)) { 3703 $obj = $ADODB_NEWCONNECTION($db); 3704 3705 } else { 3706 3707 if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = ''; 3708 if (empty($db)) $db = $ADODB_LASTDB; 3709 3710 if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db); 3711 3712 if (!$db) { 3713 if (isset($origdsn)) $db = $origdsn; 3714 if ($errorfn) { 3715 // raise an error 3716 $ignore = false; 3717 $errorfn('ADONewConnection', 'ADONewConnection', -998, 3718 "could not load the database driver for '$db'", 3719 $db,false,$ignore); 3720 } else 3721 ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false); 3722 3723 return $false; 3724 } 3725 3726 $cls = 'ADODB_'.$db; 3727 if (!class_exists($cls)) { 3728 adodb_backtrace(); 3729 return $false; 3730 } 3731 3732 $obj = new $cls(); 3733 } 3734 3735 # constructor should not fail 3736 if ($obj) { 3737 if ($errorfn) $obj->raiseErrorFn = $errorfn; 3738 if (isset($dsna)) { 3739 if (isset($dsna['port'])) $obj->port = $dsna['port']; 3740 foreach($opt as $k => $v) { 3741 switch(strtolower($k)) { 3742 case 'persist': 3743 case 'persistent': $persist = $v; break; 3744 case 'debug': $obj->debug = (integer) $v; break; 3745 #ibase 3746 case 'role': $obj->role = $v; break; 3747 case 'dialect': $obj->dialect = (integer) $v; break; 3748 case 'charset': $obj->charset = $v; $obj->charSet=$v; break; 3749 case 'buffers': $obj->buffers = $v; break; 3750 case 'fetchmode': $obj->SetFetchMode($v); break; 3751 #ado 3752 case 'charpage': $obj->charPage = $v; break; 3753 #mysql, mysqli 3754 case 'clientflags': $obj->clientFlags = $v; break; 3755 #mysql, mysqli, postgres 3756 case 'port': $obj->port = $v; break; 3757 #mysqli 3758 case 'socket': $obj->socket = $v; break; 3759 #oci8 3760 case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break; 3761 } 3762 } 3763 if (empty($persist)) 3764 $ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']); 3765 else 3766 $ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']); 3767 3768 if (!$ok) return $false; 3769 } 3770 } 3771 return $obj; 3772 } 3773 3774 3775 3776 // $perf == true means called by NewPerfMonitor(), otherwise for data dictionary 3777 function _adodb_getdriver($provider,$drivername,$perf=false) 3778 { 3779 switch ($provider) { 3780 case 'odbtp': if (strncmp('odbtp_',$drivername,6)==0) return substr($drivername,6); 3781 case 'odbc' : if (strncmp('odbc_',$drivername,5)==0) return substr($drivername,5); 3782 case 'ado' : if (strncmp('ado_',$drivername,4)==0) return substr($drivername,4); 3783 case 'native': break; 3784 default: 3785 return $provider; 3786 } 3787 3788 switch($drivername) { 3789 case 'firebird15': $drivername = 'firebird'; break; 3790 case 'oracle': $drivername = 'oci8'; break; 3791 case 'access': if ($perf) $drivername = ''; break; 3792 case 'db2' : break; 3793 case 'sapdb' : break; 3794 default: 3795 $drivername = 'generic'; 3796 break; 3797 } 3798 return $drivername; 3799 } 3800 3801 function &NewPerfMonitor(&$conn) 3802 { 3803 $false = false; 3804 $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true); 3805 if (!$drivername || $drivername == 'generic') return $false; 3806 include_once(ADODB_DIR.'/adodb-perf.inc.php'); 3807 @include_once(ADODB_DIR."/perf/perf-$drivername.inc.php"); 3808 $class = "Perf_$drivername"; 3809 if (!class_exists($class)) return $false; 3810 $perf = new $class($conn); 3811 3812 return $perf; 3813 } 3814 3815 function &NewDataDictionary(&$conn,$drivername=false) 3816 { 3817 $false = false; 3818 if (!$drivername) $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType); 3819 3820 include_once(ADODB_DIR.'/adodb-lib.inc.php'); 3821 include_once(ADODB_DIR.'/adodb-datadict.inc.php'); 3822 $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php"; 3823 3824 if (!file_exists($path)) { 3825 ADOConnection::outp("Database driver '$path' not available"); 3826 return $false; 3827 } 3828 include_once($path); 3829 $class = "ADODB2_$drivername"; 3830 $dict = new $class(); 3831 $dict->dataProvider = $conn->dataProvider; 3832 $dict->connection = &$conn; 3833 $dict->upperName = strtoupper($drivername); 3834 $dict->quote = $conn->nameQuote; 3835 if (!empty($conn->_connectionID)) 3836 $dict->serverInfo = $conn->ServerInfo(); 3837 3838 return $dict; 3839 } 3840 3841 3842 3843 /* 3844 Perform a print_r, with pre tags for better formatting. 3845 */ 3846 function adodb_pr($var,$as_string=false) 3847 { 3848 if ($as_string) ob_start(); 3849 3850 if (isset($_SERVER['HTTP_USER_AGENT'])) { 3851 echo " <pre>\n";print_r($var);echo "</pre>\n"; 3852 } else 3853 print_r($var); 3854 3855 if ($as_string) { 3856 $s = ob_get_contents(); 3857 ob_end_clean(); 3858 return $s; 3859 } 3860 } 3861 3862 /* 3863 Perform a stack-crawl and pretty print it. 3864 3865 @param printOrArr Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then). 3866 @param levels Number of levels to display 3867 */ 3868 function adodb_backtrace($printOrArr=true,$levels=9999) 3869 { 3870 global $ADODB_INCLUDED_LIB; 3871 if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php'); 3872 return _adodb_backtrace($printOrArr,$levels); 3873 } 3874 3875} // defined 3876?>