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