MDL-49144 blocks: Add behat test to ensury sanity of block title
[moodle.git] / lib / adodb / adodb.inc.php
blobc6f08bc621e2f10549dc241a17119ac2542a9dae
1 <?php
2 /*
3 * Set tabs to 4 for best viewing.
5 * Latest version is available at http://adodb.sourceforge.net
7 * This is the main include file for ADOdb.
8 * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
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/
14 /**
15 \mainpage
17 @version V5.19 23-Apr-2014 (c) 2000-2014 John Lim (jlim#natsoft.com). All rights reserved.
19 Released under both BSD license and Lesser GPL library license. You can choose which license
20 you prefer.
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.
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.
31 Latest Download at http://adodb.sourceforge.net/
35 if (!defined('_ADODB_LAYER')) {
36 define('_ADODB_LAYER',1);
38 //==============================================================================================
39 // CONSTANT DEFINITIONS
40 //==============================================================================================
43 /**
44 * Set ADODB_DIR to the directory where this file resides...
45 * This constant was formerly called $ADODB_RootPath
47 if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
49 //==============================================================================================
50 // GLOBAL VARIABLES
51 //==============================================================================================
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_CACHE,
58 $ADODB_CACHE_CLASS,
59 $ADODB_EXTENSION, // ADODB extension installed
60 $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
61 $ADODB_FETCH_MODE, // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
62 $ADODB_GETONE_EOF,
63 $ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.
65 //==============================================================================================
66 // GLOBAL SETUP
67 //==============================================================================================
69 $ADODB_EXTENSION = defined('ADODB_EXTENSION');
71 // ********************************************************
72 // Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
73 // Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
75 // 0 = ignore empty fields. All empty fields in array are ignored.
76 // 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
77 // 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
78 // 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.
80 define('ADODB_FORCE_IGNORE',0);
81 define('ADODB_FORCE_NULL',1);
82 define('ADODB_FORCE_EMPTY',2);
83 define('ADODB_FORCE_VALUE',3);
84 // ********************************************************
87 if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
89 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
91 // allow [ ] @ ` " and . in table names
92 define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
94 // prefetching used by oracle
95 if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
99 Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names.
100 This currently works only with mssql, odbc, oci8po and ibase derived drivers.
102 0 = assoc lowercase field names. $rs->fields['orderid']
103 1 = assoc uppercase field names. $rs->fields['ORDERID']
104 2 = use native-case field names. $rs->fields['OrderID']
106 define('ADODB_ASSOC_CASE_LOWER', 0);
107 define('ADODB_ASSOC_CASE_UPPER', 1);
108 define('ADODB_ASSOC_CASE_NATIVE', 2);
110 define('ADODB_FETCH_DEFAULT',0);
111 define('ADODB_FETCH_NUM',1);
112 define('ADODB_FETCH_ASSOC',2);
113 define('ADODB_FETCH_BOTH',3);
115 if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100);
117 // PHP's version scheme makes converting to numbers difficult - workaround
118 $_adodb_ver = (float) PHP_VERSION;
119 if ($_adodb_ver >= 5.2) {
120 define('ADODB_PHPVER',0x5200);
121 } else if ($_adodb_ver >= 5.0) {
122 define('ADODB_PHPVER',0x5000);
123 } else
124 die("PHP5 or later required. You are running ".PHP_VERSION);
128 //if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
132 Accepts $src and $dest arrays, replacing string $data
134 function ADODB_str_replace($src, $dest, $data)
136 if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data);
138 $s = reset($src);
139 $d = reset($dest);
140 while ($s !== false) {
141 $data = str_replace($s,$d,$data);
142 $s = next($src);
143 $d = next($dest);
145 return $data;
148 function ADODB_Setup()
150 GLOBAL
151 $ADODB_vers, // database version
152 $ADODB_COUNTRECS, // count number of records returned - slows down query
153 $ADODB_CACHE_DIR, // directory to cache recordsets
154 $ADODB_FETCH_MODE,
155 $ADODB_CACHE,
156 $ADODB_CACHE_CLASS,
157 $ADODB_FORCE_TYPE,
158 $ADODB_GETONE_EOF,
159 $ADODB_QUOTE_FIELDNAMES;
161 if (empty($ADODB_CACHE_CLASS)) $ADODB_CACHE_CLASS = 'ADODB_Cache_File' ;
162 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
163 $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
164 $ADODB_GETONE_EOF = null;
166 if (!isset($ADODB_CACHE_DIR)) {
167 $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
168 } else {
169 // do not accept url based paths, eg. http:/ or ftp:/
170 if (strpos($ADODB_CACHE_DIR,'://') !== false)
171 die("Illegal path http:// or ftp://");
175 // Initialize random number generator for randomizing cache flushes
176 // -- note Since PHP 4.2.0, the seed becomes optional and defaults to a random value if omitted.
177 // MDL-41198 Removed random seed initialization.
178 // srand(((double)microtime())*1000000);
181 * ADODB version as a string.
183 $ADODB_vers = 'V5.19 23-Apr-2014 (c) 2000-2014 John Lim (jlim#natsoft.com). All rights reserved. Released BSD & LGPL.';
186 * Determines whether recordset->RecordCount() is used.
187 * Set to false for highest performance -- RecordCount() will always return -1 then
188 * for databases that provide "virtual" recordcounts...
190 if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true;
194 //==============================================================================================
195 // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
196 //==============================================================================================
198 ADODB_Setup();
200 //==============================================================================================
201 // CLASS ADOFieldObject
202 //==============================================================================================
204 * Helper class for FetchFields -- holds info on a column
206 class ADOFieldObject {
207 var $name = '';
208 var $max_length=0;
209 var $type="";
211 // additional fields by dannym... (danny_milo@yahoo.com)
212 var $not_null = false;
213 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
214 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
216 var $has_default = false; // this one I have done only in mysql and postgres for now ...
217 // others to come (dannym)
218 var $default_value; // default, if any, and supported. Check has_default first.
223 function _adodb_safedate($s)
225 return str_replace(array("'", '\\'), '', $s);
228 // parse date string to prevent injection attack
229 // date string will have one quote at beginning e.g. '3434343'
230 function _adodb_safedateq($s)
232 $len = strlen($s);
233 if ($s[0] !== "'") $s2 = "'".$s[0];
234 else $s2 = "'";
235 for($i=1; $i<$len; $i++) {
236 $ch = $s[$i];
237 if ($ch === '\\') {
238 $s2 .= "'";
239 break;
240 } elseif ($ch === "'") {
241 $s2 .= $ch;
242 break;
245 $s2 .= $ch;
248 return strlen($s2) == 0 ? 'null' : $s2;
252 // for transaction handling
254 function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
256 //print "Errorno ($fn errno=$errno m=$errmsg) ";
257 $thisConnection->_transOK = false;
258 if ($thisConnection->_oldRaiseFn) {
259 $fn = $thisConnection->_oldRaiseFn;
260 $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
264 //------------------
265 // class for caching
266 class ADODB_Cache_File {
268 var $createdir = true; // requires creation of temp dirs
270 function ADODB_Cache_File()
272 global $ADODB_INCLUDED_CSV;
273 if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
276 // write serialised recordset to cache item/file
277 function writecache($filename, $contents, $debug, $secs2cache)
279 return adodb_write_file($filename, $contents,$debug);
282 // load serialised recordset and unserialise it
283 function &readcache($filename, &$err, $secs2cache, $rsClass)
285 $rs = csv2rs($filename,$err,$secs2cache,$rsClass);
286 return $rs;
289 // flush all items in cache
290 function flushall($debug=false)
292 global $ADODB_CACHE_DIR;
294 $rez = false;
296 if (strlen($ADODB_CACHE_DIR) > 1) {
297 $rez = $this->_dirFlush($ADODB_CACHE_DIR);
298 if ($debug) ADOConnection::outp( "flushall: $dir<br><pre>\n". $rez."</pre>");
300 return $rez;
303 // flush one file in cache
304 function flushcache($f, $debug=false)
306 if (!@unlink($f)) {
307 if ($debug) ADOConnection::outp( "flushcache: failed for $f");
311 function getdirname($hash)
313 global $ADODB_CACHE_DIR;
314 if (!isset($this->notSafeMode)) $this->notSafeMode = !ini_get('safe_mode');
315 return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
318 // create temp directories
319 function createdir($hash, $debug)
321 global $ADODB_CACHE_PERMS;
323 $dir = $this->getdirname($hash);
324 if ($this->notSafeMode && !file_exists($dir)) {
325 $oldu = umask(0);
326 if (!@mkdir($dir, empty($ADODB_CACHE_PERMS) ? 0771 : $ADODB_CACHE_PERMS)) if(!is_dir($dir) && $debug) ADOConnection::outp("Cannot create $dir");
327 umask($oldu);
330 return $dir;
334 * Private function to erase all of the files and subdirectories in a directory.
336 * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
337 * Note: $kill_top_level is used internally in the function to flush subdirectories.
339 function _dirFlush($dir, $kill_top_level = false)
341 if(!$dh = @opendir($dir)) return;
343 while (($obj = readdir($dh))) {
344 if($obj=='.' || $obj=='..') continue;
345 $f = $dir.'/'.$obj;
347 if (strpos($obj,'.cache')) @unlink($f);
348 if (is_dir($f)) $this->_dirFlush($f, true);
350 if ($kill_top_level === true) @rmdir($dir);
351 return true;
355 //==============================================================================================
356 // CLASS ADOConnection
357 //==============================================================================================
360 * Connection object. For connecting to databases, and executing queries.
362 abstract class ADOConnection {
364 // PUBLIC VARS
366 var $dataProvider = 'native';
367 var $databaseType = ''; /// RDBMS currently in use, eg. odbc, mysql, mssql
368 var $database = ''; /// Name of database to be used.
369 var $host = ''; /// The hostname of the database server
370 var $user = ''; /// The username which is used to connect to the database server.
371 var $password = ''; /// Password for the username. For security, we no longer store it.
372 var $debug = false; /// if set to true will output sql statements
373 var $maxblobsize = 262144; /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
374 var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
375 var $substr = 'substr'; /// substring operator
376 var $length = 'length'; /// string length ofperator
377 var $random = 'rand()'; /// random function
378 var $upperCase = 'upper'; /// uppercase function
379 var $fmtDate = "'Y-m-d'"; /// used by DBDate() as the default date format used by the database
380 var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
381 var $true = '1'; /// string that represents TRUE for a database
382 var $false = '0'; /// string that represents FALSE for a database
383 var $replaceQuote = "\\'"; /// string to use to replace quotes
384 var $nameQuote = '"'; /// string to use to quote identifiers and names
385 var $charSet=false; /// character set to use - only for interbase, postgres and oci8
386 var $metaDatabasesSQL = '';
387 var $metaTablesSQL = '';
388 var $uniqueOrderBy = false; /// All order by columns have to be unique
389 var $emptyDate = '&nbsp;';
390 var $emptyTimeStamp = '&nbsp;';
391 var $lastInsID = false;
392 //--
393 var $hasInsertID = false; /// supports autoincrement ID?
394 var $hasAffectedRows = false; /// supports affected rows for update/delete?
395 var $hasTop = false; /// support mssql/access SELECT TOP 10 * FROM TABLE
396 var $hasLimit = false; /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
397 var $readOnly = false; /// this is a readonly database - used by phpLens
398 var $hasMoveFirst = false; /// has ability to run MoveFirst(), scrolling backwards
399 var $hasGenID = false; /// can generate sequences using GenID();
400 var $hasTransactions = true; /// has transactions
401 //--
402 var $genID = 0; /// sequence id used by GenID();
403 var $raiseErrorFn = false; /// error function to call
404 var $isoDates = false; /// accepts dates in ISO format
405 var $cacheSecs = 3600; /// cache for 1 hour
407 // memcache
408 var $memCache = false; /// should we use memCache instead of caching in files
409 var $memCacheHost; /// memCache host
410 var $memCachePort = 11211; /// memCache port
411 var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
413 var $sysDate = false; /// name of function that returns the current date
414 var $sysTimeStamp = false; /// name of function that returns the current timestamp
415 var $sysUTimeStamp = false; // name of function that returns the current timestamp accurate to the microsecond or nearest fraction
416 var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
418 var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
419 var $numCacheHits = 0;
420 var $numCacheMisses = 0;
421 var $pageExecuteCountRows = true;
422 var $uniqueSort = false; /// indicates that all fields in order by must be unique
423 var $leftOuter = false; /// operator to use for left outer join in WHERE clause
424 var $rightOuter = false; /// operator to use for right outer join in WHERE clause
425 var $ansiOuter = false; /// whether ansi outer join syntax supported
426 var $autoRollback = false; // autoRollback on PConnect().
427 var $poorAffectedRows = false; // affectedRows not working or unreliable
429 var $fnExecute = false;
430 var $fnCacheExecute = false;
431 var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
432 var $rsPrefix = "ADORecordSet_";
434 var $autoCommit = true; /// do not modify this yourself - actually private
435 var $transOff = 0; /// temporarily disable transactions
436 var $transCnt = 0; /// count of nested transactions
438 var $fetchMode=false;
440 var $null2null = 'null'; // in autoexecute/getinsertsql/getupdatesql, this value will be converted to a null
441 var $bulkBind = false; // enable 2D Execute array
443 // PRIVATE VARS
445 var $_oldRaiseFn = false;
446 var $_transOK = null;
447 var $_connectionID = false; /// The returned link identifier whenever a successful database connection is made.
448 var $_errorMsg = false; /// A variable which was used to keep the returned last error message. The value will
449 /// then returned by the errorMsg() function
450 var $_errorCode = false; /// Last error code, not guaranteed to be used - only by oci8
451 var $_queryID = false; /// This variable keeps the last created result link identifier
453 var $_isPersistentConnection = false; /// A boolean variable to state whether its a persistent connection or normal connection. */
454 var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
455 var $_evalAll = false;
456 var $_affected = false;
457 var $_logsql = false;
458 var $_transmode = ''; // transaction mode
461 static function Version()
463 global $ADODB_vers;
465 $ok = preg_match( '/^[Vv]?([0-9]\.[0-9]+(dev|[a-z]))?/', $ADODB_vers, $matches );
466 if (!$ok) return (float) substr($ADODB_vers,1);
467 else return $matches[1];
471 Get server version info...
473 @returns An array with 2 elements: $arr['string'] is the description string,
474 and $arr[version] is the version (also a string).
476 function ServerInfo()
478 return array('description' => '', 'version' => '');
481 function IsConnected()
483 return !empty($this->_connectionID);
486 function _findvers($str)
488 if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1];
489 else return '';
493 * All error messages go through this bottleneck function.
494 * You can define your own handler by defining the function name in ADODB_OUTP.
496 static function outp($msg,$newline=true)
498 global $ADODB_FLUSH,$ADODB_OUTP;
500 if (defined('ADODB_OUTP')) {
501 $fn = ADODB_OUTP;
502 $fn($msg,$newline);
503 return;
504 } else if (isset($ADODB_OUTP)) {
505 $fn = $ADODB_OUTP;
506 $fn($msg,$newline);
507 return;
510 if ($newline) $msg .= "<br>\n";
512 if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg;
513 else echo strip_tags($msg);
516 if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); // do not flush if output buffering enabled - useless - thx to Jesse Mullan
520 function Time()
522 $rs = $this->_Execute("select $this->sysTimeStamp");
523 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
525 return false;
529 * Connect to database
531 * @param [argHostname] Host to connect to
532 * @param [argUsername] Userid to login
533 * @param [argPassword] Associated password
534 * @param [argDatabaseName] database
535 * @param [forceNew] force new connection
537 * @return true or false
539 function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false)
541 if ($argHostname != "") $this->host = $argHostname;
542 if ($argUsername != "") $this->user = $argUsername;
543 if ($argPassword != "") $this->password = 'not stored'; // not stored for security reasons
544 if ($argDatabaseName != "") $this->database = $argDatabaseName;
546 $this->_isPersistentConnection = false;
548 if ($forceNew) {
549 if ($rez=$this->_nconnect($this->host, $this->user, $argPassword, $this->database)) return true;
550 } else {
551 if ($rez=$this->_connect($this->host, $this->user, $argPassword, $this->database)) return true;
553 if (isset($rez)) {
554 $err = $this->ErrorMsg();
555 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
556 $ret = false;
557 } else {
558 $err = "Missing extension for ".$this->dataProvider;
559 $ret = 0;
561 if ($fn = $this->raiseErrorFn)
562 $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
565 $this->_connectionID = false;
566 if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
567 return $ret;
570 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
572 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
577 * Always force a new connection to database - currently only works with oracle
579 * @param [argHostname] Host to connect to
580 * @param [argUsername] Userid to login
581 * @param [argPassword] Associated password
582 * @param [argDatabaseName] database
584 * @return true or false
586 function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
588 return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
592 * Establish persistent connect to database
594 * @param [argHostname] Host to connect to
595 * @param [argUsername] Userid to login
596 * @param [argPassword] Associated password
597 * @param [argDatabaseName] database
599 * @return return true or false
601 function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
604 if (defined('ADODB_NEVER_PERSIST'))
605 return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
607 if ($argHostname != "") $this->host = $argHostname;
608 if ($argUsername != "") $this->user = $argUsername;
609 if ($argPassword != "") $this->password = 'not stored';
610 if ($argDatabaseName != "") $this->database = $argDatabaseName;
612 $this->_isPersistentConnection = true;
614 if ($rez = $this->_pconnect($this->host, $this->user, $argPassword, $this->database)) return true;
615 if (isset($rez)) {
616 $err = $this->ErrorMsg();
617 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
618 $ret = false;
619 } else {
620 $err = "Missing extension for ".$this->dataProvider;
621 $ret = 0;
623 if ($fn = $this->raiseErrorFn) {
624 $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
627 $this->_connectionID = false;
628 if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
629 return $ret;
632 function outp_throw($msg,$src='WARN',$sql='')
634 if (defined('ADODB_ERROR_HANDLER') && ADODB_ERROR_HANDLER == 'adodb_throw') {
635 adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
636 return;
638 ADOConnection::outp($msg);
641 // create cache class. Code is backward compat with old memcache implementation
642 function _CreateCache()
644 global $ADODB_CACHE, $ADODB_CACHE_CLASS;
646 if ($this->memCache) {
647 global $ADODB_INCLUDED_MEMCACHE;
649 if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
650 $ADODB_CACHE = new ADODB_Cache_MemCache($this);
651 } else
652 $ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
656 // Format date column in sql string given an input format that understands Y M D
657 function SQLDate($fmt, $col=false)
659 if (!$col) $col = $this->sysDate;
660 return $col; // child class implement
664 * Should prepare the sql statement and return the stmt resource.
665 * For databases that do not support this, we return the $sql. To ensure
666 * compatibility with databases that do not support prepare:
668 * $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
669 * $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
670 * $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
672 * @param sql SQL to send to database
674 * @return return FALSE, or the prepared statement, or the original sql if
675 * if the database does not support prepare.
678 function Prepare($sql)
680 return $sql;
684 * Some databases, eg. mssql require a different function for preparing
685 * stored procedures. So we cannot use Prepare().
687 * Should prepare the stored procedure and return the stmt resource.
688 * For databases that do not support this, we return the $sql. To ensure
689 * compatibility with databases that do not support prepare:
691 * @param sql SQL to send to database
693 * @return return FALSE, or the prepared statement, or the original sql if
694 * if the database does not support prepare.
697 function PrepareSP($sql,$param=true)
699 return $this->Prepare($sql,$param);
703 * PEAR DB Compat
705 function Quote($s)
707 return $this->qstr($s,false);
711 Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
713 function QMagic($s)
715 return $this->qstr($s,get_magic_quotes_gpc());
718 function q(&$s)
720 #if (!empty($this->qNull)) if ($s == 'null') return $s;
721 $s = $this->qstr($s,false);
725 * PEAR DB Compat - do not use internally.
727 function ErrorNative()
729 return $this->ErrorNo();
734 * PEAR DB Compat - do not use internally.
736 function nextId($seq_name)
738 return $this->GenID($seq_name);
742 * Lock a row, will escalate and lock the table if row locking not supported
743 * will normally free the lock at the end of the transaction
745 * @param $table name of table to lock
746 * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
748 function RowLock($table,$where,$col='1 as adodbignore')
750 return false;
753 function CommitLock($table)
755 return $this->CommitTrans();
758 function RollbackLock($table)
760 return $this->RollbackTrans();
764 * PEAR DB Compat - do not use internally.
766 * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
767 * for easy porting :-)
769 * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
770 * @returns The previous fetch mode
772 function SetFetchMode($mode)
774 $old = $this->fetchMode;
775 $this->fetchMode = $mode;
777 if ($old === false) {
778 global $ADODB_FETCH_MODE;
779 return $ADODB_FETCH_MODE;
781 return $old;
786 * PEAR DB Compat - do not use internally.
788 function Query($sql, $inputarr=false)
790 $rs = $this->Execute($sql, $inputarr);
791 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
792 return $rs;
797 * PEAR DB Compat - do not use internally
799 function LimitQuery($sql, $offset, $count, $params=false)
801 $rs = $this->SelectLimit($sql, $count, $offset, $params);
802 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
803 return $rs;
808 * PEAR DB Compat - do not use internally
810 function Disconnect()
812 return $this->Close();
816 * Returns a placeholder for query parameters
817 * e.g. $DB->Param('a') will return
818 * - '?' for most databases
819 * - ':a' for Oracle
820 * - '$1', '$2', etc. for PostgreSQL
821 * @param string $name parameter's name, false to force a reset of the
822 * number to 1 (for databases that require positioned
823 * params such as PostgreSQL; note that ADOdb will
824 * automatically reset this when executing a query )
825 * @param string $type (unused)
826 * @return string query parameter placeholder
828 function Param($name,$type='C')
830 return '?';
834 InParameter and OutParameter are self-documenting versions of Parameter().
836 function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
838 return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
843 function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
845 return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
851 Usage in oracle
852 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
853 $db->Parameter($stmt,$id,'myid');
854 $db->Parameter($stmt,$group,'group',64);
855 $db->Execute();
857 @param $stmt Statement returned by Prepare() or PrepareSP().
858 @param $var PHP variable to bind to
859 @param $name Name of stored procedure variable name to bind to.
860 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
861 @param [$maxLen] Holds an maximum length of the variable.
862 @param [$type] The data type of $var. Legal values depend on driver.
865 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
867 return false;
871 function IgnoreErrors($saveErrs=false)
873 if (!$saveErrs) {
874 $saveErrs = array($this->raiseErrorFn,$this->_transOK);
875 $this->raiseErrorFn = false;
876 return $saveErrs;
877 } else {
878 $this->raiseErrorFn = $saveErrs[0];
879 $this->_transOK = $saveErrs[1];
884 Improved method of initiating a transaction. Used together with CompleteTrans().
885 Advantages include:
887 a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
888 Only the outermost block is treated as a transaction.<br>
889 b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
890 c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
891 are disabled, making it backward compatible.
893 function StartTrans($errfn = 'ADODB_TransMonitor')
895 if ($this->transOff > 0) {
896 $this->transOff += 1;
897 return true;
900 $this->_oldRaiseFn = $this->raiseErrorFn;
901 $this->raiseErrorFn = $errfn;
902 $this->_transOK = true;
904 if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
905 $ok = $this->BeginTrans();
906 $this->transOff = 1;
907 return $ok;
912 Used together with StartTrans() to end a transaction. Monitors connection
913 for sql errors, and will commit or rollback as appropriate.
915 @autoComplete if true, monitor sql errors and commit and rollback as appropriate,
916 and if set to false force rollback even if no SQL error detected.
917 @returns true on commit, false on rollback.
919 function CompleteTrans($autoComplete = true)
921 if ($this->transOff > 1) {
922 $this->transOff -= 1;
923 return true;
925 $this->raiseErrorFn = $this->_oldRaiseFn;
927 $this->transOff = 0;
928 if ($this->_transOK && $autoComplete) {
929 if (!$this->CommitTrans()) {
930 $this->_transOK = false;
931 if ($this->debug) ADOConnection::outp("Smart Commit failed");
932 } else
933 if ($this->debug) ADOConnection::outp("Smart Commit occurred");
934 } else {
935 $this->_transOK = false;
936 $this->RollbackTrans();
937 if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred");
940 return $this->_transOK;
944 At the end of a StartTrans/CompleteTrans block, perform a rollback.
946 function FailTrans()
948 if ($this->debug)
949 if ($this->transOff == 0) {
950 ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
951 } else {
952 ADOConnection::outp("FailTrans was called");
953 adodb_backtrace();
955 $this->_transOK = false;
959 Check if transaction has failed, only for Smart Transactions.
961 function HasFailedTrans()
963 if ($this->transOff > 0) return $this->_transOK == false;
964 return false;
968 * Execute SQL
970 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
971 * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
972 * @return RecordSet or false
974 function Execute($sql,$inputarr=false)
976 if ($this->fnExecute) {
977 $fn = $this->fnExecute;
978 $ret = $fn($this,$sql,$inputarr);
979 if (isset($ret)) return $ret;
981 if ($inputarr !== false) {
982 if (!is_array($inputarr)) $inputarr = array($inputarr);
984 $element0 = reset($inputarr);
985 # is_object check because oci8 descriptors can be passed in
986 $array_2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
988 //remove extra memory copy of input -mikefedyk
989 unset($element0);
991 if (!is_array($sql) && !$this->_bindInputArray) {
992 $sqlarr = explode('?',$sql);
993 $nparams = sizeof($sqlarr)-1;
994 if (!$array_2d) $inputarr = array($inputarr);
996 foreach($inputarr as $arr) {
997 $sql = ''; $i = 0;
998 //Use each() instead of foreach to reduce memory usage -mikefedyk
999 while(list(, $v) = each($arr)) {
1000 $sql .= $sqlarr[$i];
1001 // from Ron Baldwin <ron.baldwin#sourceprose.com>
1002 // Only quote string types
1003 $typ = gettype($v);
1004 if ($typ == 'string')
1005 //New memory copy of input created here -mikefedyk
1006 $sql .= $this->qstr($v);
1007 else if ($typ == 'double')
1008 $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
1009 else if ($typ == 'boolean')
1010 $sql .= $v ? $this->true : $this->false;
1011 else if ($typ == 'object') {
1012 if (method_exists($v, '__toString')) $sql .= $this->qstr($v->__toString());
1013 else $sql .= $this->qstr((string) $v);
1014 } else if ($v === null)
1015 $sql .= 'NULL';
1016 else
1017 $sql .= $v;
1018 $i += 1;
1020 if ($i == $nparams) break;
1021 } // while
1022 if (isset($sqlarr[$i])) {
1023 $sql .= $sqlarr[$i];
1024 if ($i+1 != sizeof($sqlarr)) $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
1025 } else if ($i != sizeof($sqlarr))
1026 $this->outp_throw( "Input array does not match ?: ".htmlspecialchars($sql),'Execute');
1028 $ret = $this->_Execute($sql);
1029 if (!$ret) return $ret;
1031 } else {
1032 if ($array_2d) {
1033 if (is_string($sql))
1034 $stmt = $this->Prepare($sql);
1035 else
1036 $stmt = $sql;
1038 foreach($inputarr as $arr) {
1039 $ret = $this->_Execute($stmt,$arr);
1040 if (!$ret) return $ret;
1042 } else {
1043 $ret = $this->_Execute($sql,$inputarr);
1046 } else {
1047 $ret = $this->_Execute($sql,false);
1050 return $ret;
1054 function _Execute($sql,$inputarr=false)
1056 if ($this->debug) {
1057 global $ADODB_INCLUDED_LIB;
1058 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1059 $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
1060 } else {
1061 $this->_queryID = @$this->_query($sql,$inputarr);
1064 // ************************
1065 // OK, query executed
1066 // ************************
1068 if ($this->_queryID === false) { // error handling if query fails
1069 if ($this->debug == 99) adodb_backtrace(true,5);
1070 $fn = $this->raiseErrorFn;
1071 if ($fn) {
1072 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
1074 $false = false;
1075 return $false;
1078 if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead
1079 $rsclass = $this->rsPrefix.'empty';
1080 $rs = (class_exists($rsclass)) ? new $rsclass(): new ADORecordSet_empty();
1082 return $rs;
1085 // return real recordset from select statement
1086 $rsclass = $this->rsPrefix.$this->databaseType;
1087 $rs = new $rsclass($this->_queryID,$this->fetchMode);
1088 $rs->connection = $this; // Pablo suggestion
1089 $rs->Init();
1090 if (is_array($sql)) $rs->sql = $sql[0];
1091 else $rs->sql = $sql;
1092 if ($rs->_numOfRows <= 0) {
1093 global $ADODB_COUNTRECS;
1094 if ($ADODB_COUNTRECS) {
1095 if (!$rs->EOF) {
1096 $rs = $this->_rs2rs($rs,-1,-1,!is_array($sql));
1097 $rs->_queryID = $this->_queryID;
1098 } else
1099 $rs->_numOfRows = 0;
1102 return $rs;
1105 function CreateSequence($seqname='adodbseq',$startID=1)
1107 if (empty($this->_genSeqSQL)) return false;
1108 return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1111 function DropSequence($seqname='adodbseq')
1113 if (empty($this->_dropSeqSQL)) return false;
1114 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
1118 * Generates a sequence id and stores it in $this->genID;
1119 * GenID is only available if $this->hasGenID = true;
1121 * @param seqname name of sequence to use
1122 * @param startID if sequence does not exist, start at this ID
1123 * @return 0 if not supported, otherwise a sequence id
1125 function GenID($seqname='adodbseq',$startID=1)
1127 if (!$this->hasGenID) {
1128 return 0; // formerly returns false pre 1.60
1131 $getnext = sprintf($this->_genIDSQL,$seqname);
1133 $holdtransOK = $this->_transOK;
1135 $save_handler = $this->raiseErrorFn;
1136 $this->raiseErrorFn = '';
1137 @($rs = $this->Execute($getnext));
1138 $this->raiseErrorFn = $save_handler;
1140 if (!$rs) {
1141 $this->_transOK = $holdtransOK; //if the status was ok before reset
1142 $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1143 $rs = $this->Execute($getnext);
1145 if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
1146 else $this->genID = 0; // false
1148 if ($rs) $rs->Close();
1150 return $this->genID;
1154 * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
1155 * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
1156 * @return the last inserted ID. Not all databases support this.
1158 function Insert_ID($table='',$column='')
1160 if ($this->_logsql && $this->lastInsID) return $this->lastInsID;
1161 if ($this->hasInsertID) return $this->_insertid($table,$column);
1162 if ($this->debug) {
1163 ADOConnection::outp( '<p>Insert_ID error</p>');
1164 adodb_backtrace();
1166 return false;
1171 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
1173 * @return the last inserted ID. All databases support this. But aware possible
1174 * problems in multiuser environments. Heavy test this before deploying.
1176 function PO_Insert_ID($table="", $id="")
1178 if ($this->hasInsertID){
1179 return $this->Insert_ID($table,$id);
1180 } else {
1181 return $this->GetOne("SELECT MAX($id) FROM $table");
1186 * @return # rows affected by UPDATE/DELETE
1188 function Affected_Rows()
1190 if ($this->hasAffectedRows) {
1191 if ($this->fnExecute === 'adodb_log_sql') {
1192 if ($this->_logsql && $this->_affected !== false) return $this->_affected;
1194 $val = $this->_affectedrows();
1195 return ($val < 0) ? false : $val;
1198 if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1199 return false;
1204 * @return the last error message
1206 function ErrorMsg()
1208 if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1209 else return '';
1214 * @return the last error number. Normally 0 means no error.
1216 function ErrorNo()
1218 return ($this->_errorMsg) ? -1 : 0;
1221 function MetaError($err=false)
1223 include_once(ADODB_DIR."/adodb-error.inc.php");
1224 if ($err === false) $err = $this->ErrorNo();
1225 return adodb_error($this->dataProvider,$this->databaseType,$err);
1228 function MetaErrorMsg($errno)
1230 include_once(ADODB_DIR."/adodb-error.inc.php");
1231 return adodb_errormsg($errno);
1235 * @returns an array with the primary key columns in it.
1237 function MetaPrimaryKeys($table, $owner=false)
1239 // owner not used in base class - see oci8
1240 $p = array();
1241 $objs = $this->MetaColumns($table);
1242 if ($objs) {
1243 foreach($objs as $v) {
1244 if (!empty($v->primary_key))
1245 $p[] = $v->name;
1248 if (sizeof($p)) return $p;
1249 if (function_exists('ADODB_VIEW_PRIMARYKEYS'))
1250 return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1251 return false;
1255 * @returns assoc array where keys are tables, and values are foreign keys
1257 function MetaForeignKeys($table, $owner=false, $upper=false)
1259 return false;
1262 * Choose a database to connect to. Many databases do not support this.
1264 * @param dbName is the name of the database to select
1265 * @return true or false
1267 function SelectDB($dbName)
1268 {return false;}
1272 * Will select, getting rows from $offset (1-based), for $nrows.
1273 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1274 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1275 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1276 * eg.
1277 * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1278 * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1280 * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1281 * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1283 * @param sql
1284 * @param [offset] is the row to start calculations from (1-based)
1285 * @param [nrows] is the number of rows to get
1286 * @param [inputarr] array of bind variables
1287 * @param [secs2cache] is a private parameter only used by jlim
1288 * @return the recordset ($rs->databaseType == 'array')
1290 function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
1292 if ($this->hasTop && $nrows > 0) {
1293 // suggested by Reinhard Balling. Access requires top after distinct
1294 // Informix requires first before distinct - F Riosa
1295 $ismssql = (strpos($this->databaseType,'mssql') !== false);
1296 if ($ismssql) $isaccess = false;
1297 else $isaccess = (strpos($this->databaseType,'access') !== false);
1299 if ($offset <= 0) {
1301 // access includes ties in result
1302 if ($isaccess) {
1303 $sql = preg_replace(
1304 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1306 if ($secs2cache != 0) {
1307 $ret = $this->CacheExecute($secs2cache, $sql,$inputarr);
1308 } else {
1309 $ret = $this->Execute($sql,$inputarr);
1311 return $ret; // PHP5 fix
1312 } else if ($ismssql){
1313 $sql = preg_replace(
1314 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1315 } else {
1316 $sql = preg_replace(
1317 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1319 } else {
1320 $nn = $nrows + $offset;
1321 if ($isaccess || $ismssql) {
1322 $sql = preg_replace(
1323 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1324 } else {
1325 $sql = preg_replace(
1326 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1331 // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows
1332 // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1333 global $ADODB_COUNTRECS;
1335 $savec = $ADODB_COUNTRECS;
1336 $ADODB_COUNTRECS = false;
1339 if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1340 else $rs = $this->Execute($sql,$inputarr);
1342 $ADODB_COUNTRECS = $savec;
1343 if ($rs && !$rs->EOF) {
1344 $rs = $this->_rs2rs($rs,$nrows,$offset);
1346 //print_r($rs);
1347 return $rs;
1351 * Create serializable recordset. Breaks rs link to connection.
1353 * @param rs the recordset to serialize
1355 function SerializableRS(&$rs)
1357 $rs2 = $this->_rs2rs($rs);
1358 $ignore = false;
1359 $rs2->connection = $ignore;
1361 return $rs2;
1365 * Convert database recordset to an array recordset
1366 * input recordset's cursor should be at beginning, and
1367 * old $rs will be closed.
1369 * @param rs the recordset to copy
1370 * @param [nrows] number of rows to retrieve (optional)
1371 * @param [offset] offset by number of rows (optional)
1372 * @return the new recordset
1374 function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true)
1376 if (! $rs) {
1377 $false = false;
1378 return $false;
1380 $dbtype = $rs->databaseType;
1381 if (!$dbtype) {
1382 $rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1383 return $rs;
1385 if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1386 $rs->MoveFirst();
1387 $rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1388 return $rs;
1390 $flds = array();
1391 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1392 $flds[] = $rs->FetchField($i);
1395 $arr = $rs->GetArrayLimit($nrows,$offset);
1396 //print_r($arr);
1397 if ($close) $rs->Close();
1399 $arrayClass = $this->arrayClass;
1401 $rs2 = new $arrayClass();
1402 $rs2->connection = $this;
1403 $rs2->sql = $rs->sql;
1404 $rs2->dataProvider = $this->dataProvider;
1405 $rs2->InitArrayFields($arr,$flds);
1406 $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1407 return $rs2;
1411 * Return all rows. Compat with PEAR DB
1413 function GetAll($sql, $inputarr=false)
1415 $arr = $this->GetArray($sql,$inputarr);
1416 return $arr;
1419 function GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false)
1421 $rs = $this->Execute($sql, $inputarr);
1422 if (!$rs) {
1423 $false = false;
1424 return $false;
1426 $arr = $rs->GetAssoc($force_array,$first2cols);
1427 return $arr;
1430 function CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false)
1432 if (!is_numeric($secs2cache)) {
1433 $first2cols = $force_array;
1434 $force_array = $inputarr;
1436 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
1437 if (!$rs) {
1438 $false = false;
1439 return $false;
1441 $arr = $rs->GetAssoc($force_array,$first2cols);
1442 return $arr;
1446 * Return first element of first row of sql statement. Recordset is disposed
1447 * for you.
1449 * @param sql SQL statement
1450 * @param [inputarr] input bind array
1452 function GetOne($sql,$inputarr=false)
1454 global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
1455 $crecs = $ADODB_COUNTRECS;
1456 $ADODB_COUNTRECS = false;
1458 $ret = false;
1459 $rs = $this->Execute($sql,$inputarr);
1460 if ($rs) {
1461 if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
1462 else $ret = reset($rs->fields);
1464 $rs->Close();
1466 $ADODB_COUNTRECS = $crecs;
1467 return $ret;
1470 // $where should include 'WHERE fld=value'
1471 function GetMedian($table, $field,$where = '')
1473 $total = $this->GetOne("select count(*) from $table $where");
1474 if (!$total) return false;
1476 $midrow = (integer) ($total/2);
1477 $rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
1478 if ($rs && !$rs->EOF) return reset($rs->fields);
1479 return false;
1483 function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
1485 global $ADODB_GETONE_EOF;
1486 $ret = false;
1487 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1488 if ($rs) {
1489 if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
1490 else $ret = reset($rs->fields);
1491 $rs->Close();
1494 return $ret;
1497 function GetCol($sql, $inputarr = false, $trim = false)
1500 $rs = $this->Execute($sql, $inputarr);
1501 if ($rs) {
1502 $rv = array();
1503 if ($trim) {
1504 while (!$rs->EOF) {
1505 $rv[] = trim(reset($rs->fields));
1506 $rs->MoveNext();
1508 } else {
1509 while (!$rs->EOF) {
1510 $rv[] = reset($rs->fields);
1511 $rs->MoveNext();
1514 $rs->Close();
1515 } else
1516 $rv = false;
1517 return $rv;
1520 function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false)
1522 $rs = $this->CacheExecute($secs, $sql, $inputarr);
1523 if ($rs) {
1524 $rv = array();
1525 if ($trim) {
1526 while (!$rs->EOF) {
1527 $rv[] = trim(reset($rs->fields));
1528 $rs->MoveNext();
1530 } else {
1531 while (!$rs->EOF) {
1532 $rv[] = reset($rs->fields);
1533 $rs->MoveNext();
1536 $rs->Close();
1537 } else
1538 $rv = false;
1540 return $rv;
1543 function Transpose(&$rs,$addfieldnames=true)
1545 $rs2 = $this->_rs2rs($rs);
1546 $false = false;
1547 if (!$rs2) return $false;
1549 $rs2->_transpose($addfieldnames);
1550 return $rs2;
1554 Calculate the offset of a date for a particular database and generate
1555 appropriate SQL. Useful for calculating future/past dates and storing
1556 in a database.
1558 If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1560 function OffsetDate($dayFraction,$date=false)
1562 if (!$date) $date = $this->sysDate;
1563 return '('.$date.'+'.$dayFraction.')';
1569 * @param sql SQL statement
1570 * @param [inputarr] input bind array
1572 function GetArray($sql,$inputarr=false)
1574 global $ADODB_COUNTRECS;
1576 $savec = $ADODB_COUNTRECS;
1577 $ADODB_COUNTRECS = false;
1578 $rs = $this->Execute($sql,$inputarr);
1579 $ADODB_COUNTRECS = $savec;
1580 if (!$rs)
1581 if (defined('ADODB_PEAR')) {
1582 $cls = ADODB_PEAR_Error();
1583 return $cls;
1584 } else {
1585 $false = false;
1586 return $false;
1588 $arr = $rs->GetArray();
1589 $rs->Close();
1590 return $arr;
1593 function CacheGetAll($secs2cache,$sql=false,$inputarr=false)
1595 $arr = $this->CacheGetArray($secs2cache,$sql,$inputarr);
1596 return $arr;
1599 function CacheGetArray($secs2cache,$sql=false,$inputarr=false)
1601 global $ADODB_COUNTRECS;
1603 $savec = $ADODB_COUNTRECS;
1604 $ADODB_COUNTRECS = false;
1605 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1606 $ADODB_COUNTRECS = $savec;
1608 if (!$rs)
1609 if (defined('ADODB_PEAR')) {
1610 $cls = ADODB_PEAR_Error();
1611 return $cls;
1612 } else {
1613 $false = false;
1614 return $false;
1616 $arr = $rs->GetArray();
1617 $rs->Close();
1618 return $arr;
1621 function GetRandRow($sql, $arr= false)
1623 $rezarr = $this->GetAll($sql, $arr);
1624 $sz = sizeof($rezarr);
1625 return $rezarr[abs(rand()) % $sz];
1629 * Return one row of sql statement. Recordset is disposed for you.
1630 * Note that SelectLimit should not be called.
1632 * @param sql SQL statement
1633 * @param [inputarr] input bind array
1635 function GetRow($sql,$inputarr=false)
1637 global $ADODB_COUNTRECS;
1638 $crecs = $ADODB_COUNTRECS;
1639 $ADODB_COUNTRECS = false;
1641 $rs = $this->Execute($sql,$inputarr);
1643 $ADODB_COUNTRECS = $crecs;
1644 if ($rs) {
1645 if (!$rs->EOF) $arr = $rs->fields;
1646 else $arr = array();
1647 $rs->Close();
1648 return $arr;
1651 $false = false;
1652 return $false;
1655 function CacheGetRow($secs2cache,$sql=false,$inputarr=false)
1657 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1658 if ($rs) {
1659 if (!$rs->EOF) $arr = $rs->fields;
1660 else $arr = array();
1662 $rs->Close();
1663 return $arr;
1665 $false = false;
1666 return $false;
1670 * Insert or replace a single record. Note: this is not the same as MySQL's replace.
1671 * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1672 * Also note that no table locking is done currently, so it is possible that the
1673 * record be inserted twice by two programs...
1675 * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1677 * $table table name
1678 * $fieldArray associative array of data (you must quote strings yourself).
1679 * $keyCol the primary key field name or if compound key, array of field names
1680 * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers
1681 * but does not work with dates nor SQL functions.
1682 * has_autoinc the primary key is an auto-inc field, so skip in insert.
1684 * Currently blob replace not supported
1686 * returns 0 = fail, 1 = update, 2 = insert
1689 function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false)
1691 global $ADODB_INCLUDED_LIB;
1692 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1694 return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1699 * Will select, getting rows from $offset (1-based), for $nrows.
1700 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1701 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1702 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1703 * eg.
1704 * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1705 * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1707 * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1709 * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional
1710 * @param sql
1711 * @param [offset] is the row to start calculations from (1-based)
1712 * @param [nrows] is the number of rows to get
1713 * @param [inputarr] array of bind variables
1714 * @return the recordset ($rs->databaseType == 'array')
1716 function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false)
1718 if (!is_numeric($secs2cache)) {
1719 if ($sql === false) $sql = -1;
1720 if ($offset == -1) $offset = false;
1721 // sql, nrows, offset,inputarr
1722 $rs = $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1723 } else {
1724 if ($sql === false) $this->outp_throw("Warning: \$sql missing from CacheSelectLimit()",'CacheSelectLimit');
1725 $rs = $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1727 return $rs;
1732 * Flush cached recordsets that match a particular $sql statement.
1733 * If $sql == false, then we purge all files in the cache.
1737 * Flush cached recordsets that match a particular $sql statement.
1738 * If $sql == false, then we purge all files in the cache.
1740 function CacheFlush($sql=false,$inputarr=false)
1742 global $ADODB_CACHE_DIR, $ADODB_CACHE;
1744 if (empty($ADODB_CACHE)) return false;
1746 if (!$sql) {
1747 $ADODB_CACHE->flushall($this->debug);
1748 return;
1751 $f = $this->_gencachename($sql.serialize($inputarr),false);
1752 return $ADODB_CACHE->flushcache($f, $this->debug);
1757 * Private function to generate filename for caching.
1758 * Filename is generated based on:
1760 * - sql statement
1761 * - database type (oci8, ibase, ifx, etc)
1762 * - database name
1763 * - userid
1764 * - setFetchMode (adodb 4.23)
1766 * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
1767 * Assuming that we can have 50,000 files per directory with good performance,
1768 * then we can scale to 12.8 million unique cached recordsets. Wow!
1770 function _gencachename($sql,$createdir)
1772 global $ADODB_CACHE, $ADODB_CACHE_DIR;
1774 if ($this->fetchMode === false) {
1775 global $ADODB_FETCH_MODE;
1776 $mode = $ADODB_FETCH_MODE;
1777 } else {
1778 $mode = $this->fetchMode;
1780 $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1781 if (!$ADODB_CACHE->createdir) return $m;
1782 if (!$createdir) $dir = $ADODB_CACHE->getdirname($m);
1783 else $dir = $ADODB_CACHE->createdir($m, $this->debug);
1785 return $dir.'/adodb_'.$m.'.cache';
1790 * Execute SQL, caching recordsets.
1792 * @param [secs2cache] seconds to cache data, set to 0 to force query.
1793 * This is an optional parameter.
1794 * @param sql SQL statement to execute
1795 * @param [inputarr] holds the input data to bind to
1796 * @return RecordSet or false
1798 function CacheExecute($secs2cache,$sql=false,$inputarr=false)
1800 global $ADODB_CACHE;
1802 if (empty($ADODB_CACHE)) $this->_CreateCache();
1804 if (!is_numeric($secs2cache)) {
1805 $inputarr = $sql;
1806 $sql = $secs2cache;
1807 $secs2cache = $this->cacheSecs;
1810 if (is_array($sql)) {
1811 $sqlparam = $sql;
1812 $sql = $sql[0];
1813 } else
1814 $sqlparam = $sql;
1817 $md5file = $this->_gencachename($sql.serialize($inputarr),true);
1818 $err = '';
1820 if ($secs2cache > 0){
1821 $rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
1822 $this->numCacheHits += 1;
1823 } else {
1824 $err='Timeout 1';
1825 $rs = false;
1826 $this->numCacheMisses += 1;
1829 if (!$rs) {
1830 // no cached rs found
1831 if ($this->debug) {
1832 if (get_magic_quotes_runtime() && !$this->memCache) {
1833 ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
1835 if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (this is a notice and not an error)");
1838 $rs = $this->Execute($sqlparam,$inputarr);
1840 if ($rs) {
1842 $eof = $rs->EOF;
1843 $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
1844 $rs->timeCreated = time(); // used by caching
1845 $txt = _rs2serialize($rs,false,$sql); // serialize
1847 $ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
1848 if (!$ok) {
1849 if ($ok === false) {
1850 $em = 'Cache write error';
1851 $en = -32000;
1853 if ($fn = $this->raiseErrorFn) {
1854 $fn($this->databaseType,'CacheExecute', $en, $em, $md5file,$sql,$this);
1856 } else {
1857 $em = 'Cache file locked warning';
1858 $en = -32001;
1859 // do not call error handling for just a warning
1862 if ($this->debug) ADOConnection::outp( " ".$em);
1864 if ($rs->EOF && !$eof) {
1865 $rs->MoveFirst();
1866 //$rs = csv2rs($md5file,$err);
1867 $rs->connection = $this; // Pablo suggestion
1870 } else if (!$this->memCache)
1871 $ADODB_CACHE->flushcache($md5file);
1872 } else {
1873 $this->_errorMsg = '';
1874 $this->_errorCode = 0;
1876 if ($this->fnCacheExecute) {
1877 $fn = $this->fnCacheExecute;
1878 $fn($this, $secs2cache, $sql, $inputarr);
1880 // ok, set cached object found
1881 $rs->connection = $this; // Pablo suggestion
1882 if ($this->debug){
1883 if ($this->debug == 99) adodb_backtrace();
1884 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1885 $ttl = $rs->timeCreated + $secs2cache - time();
1886 $s = is_array($sql) ? $sql[0] : $sql;
1887 if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>';
1889 ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
1892 return $rs;
1897 Similar to PEAR DB's autoExecute(), except that
1898 $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
1899 If $mode == 'UPDATE', then $where is compulsory as a safety measure.
1901 $forceUpdate means that even if the data has not changed, perform update.
1903 function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false)
1905 $false = false;
1906 $sql = 'SELECT * FROM '.$table;
1907 if ($where!==FALSE) $sql .= ' WHERE '.$where;
1908 else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) {
1909 $this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause','AutoExecute');
1910 return $false;
1913 $rs = $this->SelectLimit($sql,1);
1914 if (!$rs) return $false; // table does not exist
1915 $rs->tableName = $table;
1916 $rs->sql = $sql;
1918 switch((string) $mode) {
1919 case 'UPDATE':
1920 case '2':
1921 $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
1922 break;
1923 case 'INSERT':
1924 case '1':
1925 $sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
1926 break;
1927 default:
1928 $this->outp_throw("AutoExecute: Unknown mode=$mode",'AutoExecute');
1929 return $false;
1931 $ret = false;
1932 if ($sql) $ret = $this->Execute($sql);
1933 if ($ret) $ret = true;
1934 return $ret;
1939 * Generates an Update Query based on an existing recordset.
1940 * $arrFields is an associative array of fields with the value
1941 * that should be assigned.
1943 * Note: This function should only be used on a recordset
1944 * that is run against a single table and sql should only
1945 * be a simple select stmt with no groupby/orderby/limit
1947 * "Jonathan Younger" <jyounger@unilab.com>
1949 function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null)
1951 global $ADODB_INCLUDED_LIB;
1953 // ********************************************************
1954 // This is here to maintain compatibility
1955 // with older adodb versions. Sets force type to force nulls if $forcenulls is set.
1956 if (!isset($force)) {
1957 global $ADODB_FORCE_TYPE;
1958 $force = $ADODB_FORCE_TYPE;
1960 // ********************************************************
1962 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1963 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
1967 * Generates an Insert Query based on an existing recordset.
1968 * $arrFields is an associative array of fields with the value
1969 * that should be assigned.
1971 * Note: This function should only be used on a recordset
1972 * that is run against a single table.
1974 function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null)
1976 global $ADODB_INCLUDED_LIB;
1977 if (!isset($force)) {
1978 global $ADODB_FORCE_TYPE;
1979 $force = $ADODB_FORCE_TYPE;
1982 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1983 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
1988 * Update a blob column, given a where clause. There are more sophisticated
1989 * blob handling functions that we could have implemented, but all require
1990 * a very complex API. Instead we have chosen something that is extremely
1991 * simple to understand and use.
1993 * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
1995 * Usage to update a $blobvalue which has a primary key blob_id=1 into a
1996 * field blobtable.blobcolumn:
1998 * UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
2000 * Insert example:
2002 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2003 * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
2006 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
2008 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
2012 * Usage:
2013 * UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
2015 * $blobtype supports 'BLOB' and 'CLOB'
2017 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2018 * $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
2020 function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
2022 $fd = fopen($path,'rb');
2023 if ($fd === false) return false;
2024 $val = fread($fd,filesize($path));
2025 fclose($fd);
2026 return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
2029 function BlobDecode($blob)
2031 return $blob;
2034 function BlobEncode($blob)
2036 return $blob;
2039 function SetCharSet($charset)
2041 return false;
2044 function IfNull( $field, $ifNull )
2046 return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
2049 function LogSQL($enable=true)
2051 include_once(ADODB_DIR.'/adodb-perf.inc.php');
2053 if ($enable) $this->fnExecute = 'adodb_log_sql';
2054 else $this->fnExecute = false;
2056 $old = $this->_logsql;
2057 $this->_logsql = $enable;
2058 if ($enable && !$old) $this->_affected = false;
2059 return $old;
2062 function GetCharSet()
2064 return false;
2068 * Usage:
2069 * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
2071 * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
2072 * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
2074 function UpdateClob($table,$column,$val,$where)
2076 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
2079 // not the fastest implementation - quick and dirty - jlim
2080 // for best performance, use the actual $rs->MetaType().
2081 function MetaType($t,$len=-1,$fieldobj=false)
2084 if (empty($this->_metars)) {
2085 $rsclass = $this->rsPrefix.$this->databaseType;
2086 $this->_metars = new $rsclass(false,$this->fetchMode);
2087 $this->_metars->connection = $this;
2089 return $this->_metars->MetaType($t,$len,$fieldobj);
2094 * Change the SQL connection locale to a specified locale.
2095 * This is used to get the date formats written depending on the client locale.
2097 function SetDateLocale($locale = 'En')
2099 $this->locale = $locale;
2100 switch (strtoupper($locale))
2102 case 'EN':
2103 $this->fmtDate="'Y-m-d'";
2104 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2105 break;
2107 case 'US':
2108 $this->fmtDate = "'m-d-Y'";
2109 $this->fmtTimeStamp = "'m-d-Y H:i:s'";
2110 break;
2112 case 'PT_BR':
2113 case 'NL':
2114 case 'FR':
2115 case 'RO':
2116 case 'IT':
2117 $this->fmtDate="'d-m-Y'";
2118 $this->fmtTimeStamp = "'d-m-Y H:i:s'";
2119 break;
2121 case 'GE':
2122 $this->fmtDate="'d.m.Y'";
2123 $this->fmtTimeStamp = "'d.m.Y H:i:s'";
2124 break;
2126 default:
2127 $this->fmtDate="'Y-m-d'";
2128 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2129 break;
2134 * GetActiveRecordsClass Performs an 'ALL' query
2136 * @param mixed $class This string represents the class of the current active record
2137 * @param mixed $table Table used by the active record object
2138 * @param mixed $whereOrderBy Where, order, by clauses
2139 * @param mixed $bindarr
2140 * @param mixed $primkeyArr
2141 * @param array $extra Query extras: limit, offset...
2142 * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
2143 * @access public
2144 * @return void
2146 function GetActiveRecordsClass(
2147 $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
2148 $extra=array(),
2149 $relations=array())
2151 global $_ADODB_ACTIVE_DBS;
2152 ## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
2153 ## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
2154 if (!isset($_ADODB_ACTIVE_DBS))include_once(ADODB_DIR.'/adodb-active-record.inc.php');
2155 return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
2158 function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
2160 $arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2161 return $arr;
2165 * Close Connection
2167 function Close()
2169 $rez = $this->_close();
2170 $this->_connectionID = false;
2171 return $rez;
2175 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2177 * @return true if succeeded or false if database does not support transactions
2179 function BeginTrans()
2181 if ($this->debug) ADOConnection::outp("BeginTrans: Transactions not supported for this driver");
2182 return false;
2185 /* set transaction mode */
2186 function SetTransactionMode( $transaction_mode )
2188 $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2189 $this->_transmode = $transaction_mode;
2192 http://msdn2.microsoft.com/en-US/ms173763.aspx
2193 http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2194 http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2195 http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2197 function MetaTransaction($mode,$db)
2199 $mode = strtoupper($mode);
2200 $mode = str_replace('ISOLATION LEVEL ','',$mode);
2202 switch($mode) {
2204 case 'READ UNCOMMITTED':
2205 switch($db) {
2206 case 'oci8':
2207 case 'oracle':
2208 return 'ISOLATION LEVEL READ COMMITTED';
2209 default:
2210 return 'ISOLATION LEVEL READ UNCOMMITTED';
2212 break;
2214 case 'READ COMMITTED':
2215 return 'ISOLATION LEVEL READ COMMITTED';
2216 break;
2218 case 'REPEATABLE READ':
2219 switch($db) {
2220 case 'oci8':
2221 case 'oracle':
2222 return 'ISOLATION LEVEL SERIALIZABLE';
2223 default:
2224 return 'ISOLATION LEVEL REPEATABLE READ';
2226 break;
2228 case 'SERIALIZABLE':
2229 return 'ISOLATION LEVEL SERIALIZABLE';
2230 break;
2232 default:
2233 return $mode;
2238 * If database does not support transactions, always return true as data always commited
2240 * @param $ok set to false to rollback transaction, true to commit
2242 * @return true/false.
2244 function CommitTrans($ok=true)
2245 { return true;}
2249 * If database does not support transactions, rollbacks always fail, so return false
2251 * @return true/false.
2253 function RollbackTrans()
2254 { return false;}
2258 * return the databases that the driver can connect to.
2259 * Some databases will return an empty array.
2261 * @return an array of database names.
2263 function MetaDatabases()
2265 global $ADODB_FETCH_MODE;
2267 if ($this->metaDatabasesSQL) {
2268 $save = $ADODB_FETCH_MODE;
2269 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2271 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2273 $arr = $this->GetCol($this->metaDatabasesSQL);
2274 if (isset($savem)) $this->SetFetchMode($savem);
2275 $ADODB_FETCH_MODE = $save;
2277 return $arr;
2280 return false;
2284 * List procedures or functions in an array.
2285 * @param procedureNamePattern a procedure name pattern; must match the procedure name as it is stored in the database
2286 * @param catalog a catalog name; must match the catalog name as it is stored in the database;
2287 * @param schemaPattern a schema name pattern;
2289 * @return array of procedures on current database.
2291 * Array(
2292 * [name_of_procedure] => Array(
2293 * [type] => PROCEDURE or FUNCTION
2294 * [catalog] => Catalog_name
2295 * [schema] => Schema_name
2296 * [remarks] => explanatory comment on the procedure
2300 function MetaProcedures($procedureNamePattern = null, $catalog = null, $schemaPattern = null)
2302 return false;
2307 * @param ttype can either be 'VIEW' or 'TABLE' or false.
2308 * If false, both views and tables are returned.
2309 * "VIEW" returns only views
2310 * "TABLE" returns only tables
2311 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2312 * @param mask is the input mask - only supported by oci8 and postgresql
2314 * @return array of tables for current database.
2316 function MetaTables($ttype=false,$showSchema=false,$mask=false)
2318 global $ADODB_FETCH_MODE;
2321 $false = false;
2322 if ($mask) {
2323 return $false;
2325 if ($this->metaTablesSQL) {
2326 $save = $ADODB_FETCH_MODE;
2327 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2329 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2331 $rs = $this->Execute($this->metaTablesSQL);
2332 if (isset($savem)) $this->SetFetchMode($savem);
2333 $ADODB_FETCH_MODE = $save;
2335 if ($rs === false) return $false;
2336 $arr = $rs->GetArray();
2337 $arr2 = array();
2339 if ($hast = ($ttype && isset($arr[0][1]))) {
2340 $showt = strncmp($ttype,'T',1);
2343 for ($i=0; $i < sizeof($arr); $i++) {
2344 if ($hast) {
2345 if ($showt == 0) {
2346 if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]);
2347 } else {
2348 if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]);
2350 } else
2351 $arr2[] = trim($arr[$i][0]);
2353 $rs->Close();
2354 return $arr2;
2356 return $false;
2360 function _findschema(&$table,&$schema)
2362 if (!$schema && ($at = strpos($table,'.')) !== false) {
2363 $schema = substr($table,0,$at);
2364 $table = substr($table,$at+1);
2369 * List columns in a database as an array of ADOFieldObjects.
2370 * See top of file for definition of object.
2372 * @param $table table name to query
2373 * @param $normalize makes table name case-insensitive (required by some databases)
2374 * @schema is optional database schema to use - not supported by all databases.
2376 * @return array of ADOFieldObjects for current table.
2378 function MetaColumns($table,$normalize=true)
2380 global $ADODB_FETCH_MODE;
2382 $false = false;
2384 if (!empty($this->metaColumnsSQL)) {
2386 $schema = false;
2387 $this->_findschema($table,$schema);
2389 $save = $ADODB_FETCH_MODE;
2390 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2391 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2392 $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2393 if (isset($savem)) $this->SetFetchMode($savem);
2394 $ADODB_FETCH_MODE = $save;
2395 if ($rs === false || $rs->EOF) return $false;
2397 $retarr = array();
2398 while (!$rs->EOF) { //print_r($rs->fields);
2399 $fld = new ADOFieldObject();
2400 $fld->name = $rs->fields[0];
2401 $fld->type = $rs->fields[1];
2402 if (isset($rs->fields[3]) && $rs->fields[3]) {
2403 if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
2404 $fld->scale = $rs->fields[4];
2405 if ($fld->scale>0) $fld->max_length += 1;
2406 } else
2407 $fld->max_length = $rs->fields[2];
2409 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
2410 else $retarr[strtoupper($fld->name)] = $fld;
2411 $rs->MoveNext();
2413 $rs->Close();
2414 return $retarr;
2416 return $false;
2420 * List indexes on a table as an array.
2421 * @param table table name to query
2422 * @param primary true to only show primary keys. Not actually used for most databases
2424 * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2426 * Array(
2427 * [name_of_index] => Array(
2428 * [unique] => true or false
2429 * [columns] => Array(
2430 * [0] => firstname
2431 * [1] => lastname
2436 function MetaIndexes($table, $primary = false, $owner = false)
2438 $false = false;
2439 return $false;
2443 * List columns names in a table as an array.
2444 * @param table table name to query
2446 * @return array of column names for current table.
2448 function MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */)
2450 $objarr = $this->MetaColumns($table);
2451 if (!is_array($objarr)) {
2452 $false = false;
2453 return $false;
2455 $arr = array();
2456 if ($numIndexes) {
2457 $i = 0;
2458 if ($useattnum) {
2459 foreach($objarr as $v)
2460 $arr[$v->attnum] = $v->name;
2462 } else
2463 foreach($objarr as $v) $arr[$i++] = $v->name;
2464 } else
2465 foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2467 return $arr;
2471 * Different SQL databases used different methods to combine strings together.
2472 * This function provides a wrapper.
2474 * param s variable number of string parameters
2476 * Usage: $db->Concat($str1,$str2);
2478 * @return concatenated string
2480 function Concat()
2482 $arr = func_get_args();
2483 return implode($this->concat_operator, $arr);
2488 * Converts a date "d" to a string that the database can understand.
2490 * @param d a date in Unix date time format.
2492 * @return date string in database date format
2494 function DBDate($d, $isfld=false)
2496 if (empty($d) && $d !== 0) return 'null';
2497 if ($isfld) return $d;
2499 if (is_object($d)) return $d->format($this->fmtDate);
2502 if (is_string($d) && !is_numeric($d)) {
2503 if ($d === 'null') return $d;
2504 if (strncmp($d,"'",1) === 0) {
2505 $d = _adodb_safedateq($d);
2506 return $d;
2508 if ($this->isoDates) return "'$d'";
2509 $d = ADOConnection::UnixDate($d);
2512 return adodb_date($this->fmtDate,$d);
2515 function BindDate($d)
2517 $d = $this->DBDate($d);
2518 if (strncmp($d,"'",1)) return $d;
2520 return substr($d,1,strlen($d)-2);
2523 function BindTimeStamp($d)
2525 $d = $this->DBTimeStamp($d);
2526 if (strncmp($d,"'",1)) return $d;
2528 return substr($d,1,strlen($d)-2);
2533 * Converts a timestamp "ts" to a string that the database can understand.
2535 * @param ts a timestamp in Unix date time format.
2537 * @return timestamp string in database timestamp format
2539 function DBTimeStamp($ts,$isfld=false)
2541 if (empty($ts) && $ts !== 0) return 'null';
2542 if ($isfld) return $ts;
2543 if (is_object($ts)) return $ts->format($this->fmtTimeStamp);
2545 # strlen(14) allows YYYYMMDDHHMMSS format
2546 if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14))
2547 return adodb_date($this->fmtTimeStamp,$ts);
2549 if ($ts === 'null') return $ts;
2550 if ($this->isoDates && strlen($ts) !== 14) {
2551 $ts = _adodb_safedate($ts);
2552 return "'$ts'";
2554 $ts = ADOConnection::UnixTimeStamp($ts);
2555 return adodb_date($this->fmtTimeStamp,$ts);
2559 * Also in ADORecordSet.
2560 * @param $v is a date string in YYYY-MM-DD format
2562 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2564 static function UnixDate($v)
2566 if (is_object($v)) {
2567 // odbtp support
2568 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2569 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2572 if (is_numeric($v) && strlen($v) !== 8) return $v;
2573 if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
2574 ($v), $rr)) return false;
2576 if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
2577 // h-m-s-MM-DD-YY
2578 return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2583 * Also in ADORecordSet.
2584 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2586 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2588 static function UnixTimeStamp($v)
2590 if (is_object($v)) {
2591 // odbtp support
2592 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2593 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2596 if (!preg_match(
2597 "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
2598 ($v), $rr)) return false;
2600 if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
2602 // h-m-s-MM-DD-YY
2603 if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2604 return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2608 * Also in ADORecordSet.
2610 * Format database date based on user defined format.
2612 * @param v is the character date in YYYY-MM-DD format, returned by database
2613 * @param fmt is the format to apply to it, using date()
2615 * @return a date formated as user desires
2618 function UserDate($v,$fmt='Y-m-d',$gmt=false)
2620 $tt = $this->UnixDate($v);
2622 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2623 if (($tt === false || $tt == -1) && $v != false) return $v;
2624 else if ($tt == 0) return $this->emptyDate;
2625 else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2628 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2634 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format
2635 * @param fmt is the format to apply to it, using date()
2637 * @return a timestamp formated as user desires
2639 function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false)
2641 if (!isset($v)) return $this->emptyTimeStamp;
2642 # strlen(14) allows YYYYMMDDHHMMSS format
2643 if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2644 $tt = $this->UnixTimeStamp($v);
2645 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2646 if (($tt === false || $tt == -1) && $v != false) return $v;
2647 if ($tt == 0) return $this->emptyTimeStamp;
2648 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2651 function escape($s,$magic_quotes=false)
2653 return $this->addq($s,$magic_quotes);
2657 * Quotes a string, without prefixing nor appending quotes.
2659 function addq($s,$magic_quotes=false)
2661 if (!$magic_quotes) {
2663 if ($this->replaceQuote[0] == '\\'){
2664 // only since php 4.0.5
2665 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2666 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2668 return str_replace("'",$this->replaceQuote,$s);
2671 // undo magic quotes for "
2672 $s = str_replace('\\"','"',$s);
2674 if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) // ' already quoted, no need to change anything
2675 return $s;
2676 else {// change \' to '' for sybase/mssql
2677 $s = str_replace('\\\\','\\',$s);
2678 return str_replace("\\'",$this->replaceQuote,$s);
2683 * Correctly quotes a string so that all strings are escaped. We prefix and append
2684 * to the string single-quotes.
2685 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
2687 * @param s the string to quote
2688 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
2689 * This undoes the stupidity of magic quotes for GPC.
2691 * @return quoted string to be sent back to database
2693 function qstr($s,$magic_quotes=false)
2695 if (!$magic_quotes) {
2697 if ($this->replaceQuote[0] == '\\'){
2698 // only since php 4.0.5
2699 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2700 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2702 return "'".str_replace("'",$this->replaceQuote,$s)."'";
2705 // undo magic quotes for "
2706 $s = str_replace('\\"','"',$s);
2708 if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) // ' already quoted, no need to change anything
2709 return "'$s'";
2710 else {// change \' to '' for sybase/mssql
2711 $s = str_replace('\\\\','\\',$s);
2712 return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2718 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
2719 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
2720 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2722 * See docs-adodb.htm#ex8 for an example of usage.
2724 * @param sql
2725 * @param nrows is the number of rows per page to get
2726 * @param page is the page number to get (1-based)
2727 * @param [inputarr] array of bind variables
2728 * @param [secs2cache] is a private parameter only used by jlim
2729 * @return the recordset ($rs->databaseType == 'array')
2731 * NOTE: phpLens uses a different algorithm and does not use PageExecute().
2734 function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0)
2736 global $ADODB_INCLUDED_LIB;
2737 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
2738 if ($this->pageExecuteCountRows) $rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2739 else $rs = _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2740 return $rs;
2745 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
2746 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
2747 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2749 * @param secs2cache seconds to cache data, set to 0 to force query
2750 * @param sql
2751 * @param nrows is the number of rows per page to get
2752 * @param page is the page number to get (1-based)
2753 * @param [inputarr] array of bind variables
2754 * @return the recordset ($rs->databaseType == 'array')
2756 function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false)
2758 /*switch($this->dataProvider) {
2759 case 'postgres':
2760 case 'mysql':
2761 break;
2762 default: $secs2cache = 0; break;
2764 $rs = $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
2765 return $rs;
2768 } // end class ADOConnection
2772 //==============================================================================================
2773 // CLASS ADOFetchObj
2774 //==============================================================================================
2777 * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
2779 class ADOFetchObj {
2782 //==============================================================================================
2783 // CLASS ADORecordSet_empty
2784 //==============================================================================================
2786 class ADODB_Iterator_empty implements Iterator {
2788 private $rs;
2790 function __construct($rs)
2792 $this->rs = $rs;
2794 function rewind()
2798 function valid()
2800 return !$this->rs->EOF;
2803 function key()
2805 return false;
2808 function current()
2810 return false;
2813 function next()
2817 function __call($func, $params)
2819 return call_user_func_array(array($this->rs, $func), $params);
2822 function hasMore()
2824 return false;
2831 * Lightweight recordset when there are no records to be returned
2833 class ADORecordSet_empty implements IteratorAggregate
2835 var $dataProvider = 'empty';
2836 var $databaseType = false;
2837 var $EOF = true;
2838 var $_numOfRows = 0;
2839 var $fields = false;
2840 var $connection = false;
2841 function RowCount() {return 0;}
2842 function RecordCount() {return 0;}
2843 function PO_RecordCount(){return 0;}
2844 function Close(){return true;}
2845 function FetchRow() {return false;}
2846 function FieldCount(){ return 0;}
2847 function Init() {}
2848 function getIterator() {return new ADODB_Iterator_empty($this);}
2849 function GetAssoc() {return array();}
2852 //==============================================================================================
2853 // DATE AND TIME FUNCTIONS
2854 //==============================================================================================
2855 if (!defined('ADODB_DATE_VERSION')) include(ADODB_DIR.'/adodb-time.inc.php');
2857 //==============================================================================================
2858 // CLASS ADORecordSet
2859 //==============================================================================================
2861 class ADODB_Iterator implements Iterator {
2863 private $rs;
2865 function __construct($rs)
2867 $this->rs = $rs;
2869 function rewind()
2871 $this->rs->MoveFirst();
2874 function valid()
2876 return !$this->rs->EOF;
2879 function key()
2881 return $this->rs->_currentRow;
2884 function current()
2886 return $this->rs->fields;
2889 function next()
2891 $this->rs->MoveNext();
2894 function __call($func, $params)
2896 return call_user_func_array(array($this->rs, $func), $params);
2900 function hasMore()
2902 return !$this->rs->EOF;
2910 * RecordSet class that represents the dataset returned by the database.
2911 * To keep memory overhead low, this class holds only the current row in memory.
2912 * No prefetching of data is done, so the RecordCount() can return -1 ( which
2913 * means recordcount not known).
2915 class ADORecordSet implements IteratorAggregate {
2917 * public variables
2919 var $dataProvider = "native";
2920 var $fields = false; /// holds the current row data
2921 var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob
2922 /// in other words, we use a text area for editing.
2923 var $canSeek = false; /// indicates that seek is supported
2924 var $sql; /// sql text
2925 var $EOF = false; /// Indicates that the current record position is after the last record in a Recordset object.
2927 var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
2928 var $emptyDate = '&nbsp;'; /// what to display when $time==0
2929 var $debug = false;
2930 var $timeCreated=0; /// datetime in Unix format rs created -- for cached recordsets
2932 var $bind = false; /// used by Fields() to hold array - should be private?
2933 var $fetchMode; /// default fetch mode
2934 var $connection = false; /// the parent connection
2936 * private variables
2938 var $_numOfRows = -1; /** number of rows, or -1 */
2939 var $_numOfFields = -1; /** number of fields in recordset */
2940 var $_queryID = -1; /** This variable keeps the result link identifier. */
2941 var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */
2942 var $_closed = false; /** has recordset been closed */
2943 var $_inited = false; /** Init() should only be called once */
2944 var $_obj; /** Used by FetchObj */
2945 var $_names; /** Used by FetchObj */
2947 var $_currentPage = -1; /** Added by Iván Oliva to implement recordset pagination */
2948 var $_atFirstPage = false; /** Added by Iván Oliva to implement recordset pagination */
2949 var $_atLastPage = false; /** Added by Iván Oliva to implement recordset pagination */
2950 var $_lastPageNo = -1;
2951 var $_maxRecordCount = 0;
2952 var $datetime = false;
2955 * Constructor
2957 * @param queryID this is the queryID returned by ADOConnection->_query()
2960 function ADORecordSet($queryID)
2962 $this->_queryID = $queryID;
2965 function getIterator()
2967 return new ADODB_Iterator($this);
2970 /* this is experimental - i don't really know what to return... */
2971 function __toString()
2973 include_once(ADODB_DIR.'/toexport.inc.php');
2974 return _adodb_export($this,',',',',false,true);
2978 function Init()
2980 if ($this->_inited) return;
2981 $this->_inited = true;
2982 if ($this->_queryID) @$this->_initrs();
2983 else {
2984 $this->_numOfRows = 0;
2985 $this->_numOfFields = 0;
2987 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
2989 $this->_currentRow = 0;
2990 if ($this->EOF = ($this->_fetch() === false)) {
2991 $this->_numOfRows = 0; // _numOfRows could be -1
2993 } else {
2994 $this->EOF = true;
3000 * Generate a SELECT tag string from a recordset, and return the string.
3001 * If the recordset has 2 cols, we treat the 1st col as the containing
3002 * the text to display to the user, and 2nd col as the return value. Default
3003 * strings are compared with the FIRST column.
3005 * @param name name of SELECT tag
3006 * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox.
3007 * @param [blank1stItem] true to leave the 1st item in list empty
3008 * @param [multiple] true for listbox, false for popup
3009 * @param [size] #rows to show for listbox. not used by popup
3010 * @param [selectAttr] additional attributes to defined for SELECT tag.
3011 * useful for holding javascript onChange='...' handlers.
3012 & @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with
3013 * column 0 (1st col) if this is true. This is not documented.
3015 * @return HTML
3017 * changes by glen.davies@cce.ac.nz to support multiple hilited items
3019 function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
3020 $size=0, $selectAttr='',$compareFields0=true)
3022 global $ADODB_INCLUDED_LIB;
3023 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
3024 return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
3025 $size, $selectAttr,$compareFields0);
3031 * Generate a SELECT tag string from a recordset, and return the string.
3032 * If the recordset has 2 cols, we treat the 1st col as the containing
3033 * the text to display to the user, and 2nd col as the return value. Default
3034 * strings are compared with the SECOND column.
3037 function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')
3039 return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
3040 $size, $selectAttr,false);
3044 Grouped Menu
3046 function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
3047 $size=0, $selectAttr='')
3049 global $ADODB_INCLUDED_LIB;
3050 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
3051 return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
3052 $size, $selectAttr,false);
3056 * return recordset as a 2-dimensional array.
3058 * @param [nRows] is the number of rows to return. -1 means every row.
3060 * @return an array indexed by the rows (0-based) from the recordset
3062 function GetArray($nRows = -1)
3064 global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
3065 $results = adodb_getall($this,$nRows);
3066 return $results;
3068 $results = array();
3069 $cnt = 0;
3070 while (!$this->EOF && $nRows != $cnt) {
3071 $results[] = $this->fields;
3072 $this->MoveNext();
3073 $cnt++;
3075 return $results;
3078 function GetAll($nRows = -1)
3080 $arr = $this->GetArray($nRows);
3081 return $arr;
3085 * Some databases allow multiple recordsets to be returned. This function
3086 * will return true if there is a next recordset, or false if no more.
3088 function NextRecordSet()
3090 return false;
3094 * return recordset as a 2-dimensional array.
3095 * Helper function for ADOConnection->SelectLimit()
3097 * @param offset is the row to start calculations from (1-based)
3098 * @param [nrows] is the number of rows to return
3100 * @return an array indexed by the rows (0-based) from the recordset
3102 function GetArrayLimit($nrows,$offset=-1)
3104 if ($offset <= 0) {
3105 $arr = $this->GetArray($nrows);
3106 return $arr;
3109 $this->Move($offset);
3111 $results = array();
3112 $cnt = 0;
3113 while (!$this->EOF && $nrows != $cnt) {
3114 $results[$cnt++] = $this->fields;
3115 $this->MoveNext();
3118 return $results;
3123 * Synonym for GetArray() for compatibility with ADO.
3125 * @param [nRows] is the number of rows to return. -1 means every row.
3127 * @return an array indexed by the rows (0-based) from the recordset
3129 function GetRows($nRows = -1)
3131 $arr = $this->GetArray($nRows);
3132 return $arr;
3136 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
3137 * The first column is treated as the key and is not included in the array.
3138 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
3139 * $force_array == true.
3141 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
3142 * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
3143 * read the source.
3145 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
3146 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
3148 * @return an associative array indexed by the first column of the array,
3149 * or false if the data has less than 2 cols.
3151 function GetAssoc($force_array = false, $first2cols = false)
3153 global $ADODB_EXTENSION;
3155 $cols = $this->_numOfFields;
3156 if ($cols < 2) {
3157 $false = false;
3158 return $false;
3160 $numIndex = is_array($this->fields) && array_key_exists(0, $this->fields);
3161 $results = array();
3163 if (!$first2cols && ($cols > 2 || $force_array)) {
3164 if ($ADODB_EXTENSION) {
3165 if ($numIndex) {
3166 while (!$this->EOF) {
3167 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
3168 adodb_movenext($this);
3170 } else {
3171 while (!$this->EOF) {
3172 // Fix for array_slice re-numbering numeric associative keys
3173 $keys = array_slice(array_keys($this->fields), 1);
3174 $sliced_array = array();
3176 foreach($keys as $key) {
3177 $sliced_array[$key] = $this->fields[$key];
3180 $results[trim(reset($this->fields))] = $sliced_array;
3181 adodb_movenext($this);
3184 } else {
3185 if ($numIndex) {
3186 while (!$this->EOF) {
3187 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
3188 $this->MoveNext();
3190 } else {
3191 while (!$this->EOF) {
3192 // Fix for array_slice re-numbering numeric associative keys
3193 $keys = array_slice(array_keys($this->fields), 1);
3194 $sliced_array = array();
3196 foreach($keys as $key) {
3197 $sliced_array[$key] = $this->fields[$key];
3200 $results[trim(reset($this->fields))] = $sliced_array;
3201 $this->MoveNext();
3205 } else {
3206 if ($ADODB_EXTENSION) {
3207 // return scalar values
3208 if ($numIndex) {
3209 while (!$this->EOF) {
3210 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3211 $results[trim(($this->fields[0]))] = $this->fields[1];
3212 adodb_movenext($this);
3214 } else {
3215 while (!$this->EOF) {
3216 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3217 $v1 = trim(reset($this->fields));
3218 $v2 = ''.next($this->fields);
3219 $results[$v1] = $v2;
3220 adodb_movenext($this);
3223 } else {
3224 if ($numIndex) {
3225 while (!$this->EOF) {
3226 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3227 $results[trim(($this->fields[0]))] = $this->fields[1];
3228 $this->MoveNext();
3230 } else {
3231 while (!$this->EOF) {
3232 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3233 $v1 = trim(reset($this->fields));
3234 $v2 = ''.next($this->fields);
3235 $results[$v1] = $v2;
3236 $this->MoveNext();
3242 $ref = $results; # workaround accelerator incompat with PHP 4.4 :(
3243 return $ref;
3249 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format
3250 * @param fmt is the format to apply to it, using date()
3252 * @return a timestamp formated as user desires
3254 function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
3256 if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v);
3257 $tt = $this->UnixTimeStamp($v);
3258 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3259 if (($tt === false || $tt == -1) && $v != false) return $v;
3260 if ($tt === 0) return $this->emptyTimeStamp;
3261 return adodb_date($fmt,$tt);
3266 * @param v is the character date in YYYY-MM-DD format, returned by database
3267 * @param fmt is the format to apply to it, using date()
3269 * @return a date formated as user desires
3271 function UserDate($v,$fmt='Y-m-d')
3273 $tt = $this->UnixDate($v);
3274 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3275 if (($tt === false || $tt == -1) && $v != false) return $v;
3276 else if ($tt == 0) return $this->emptyDate;
3277 else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
3279 return adodb_date($fmt,$tt);
3284 * @param $v is a date string in YYYY-MM-DD format
3286 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3288 static function UnixDate($v)
3290 return ADOConnection::UnixDate($v);
3295 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
3297 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3299 static function UnixTimeStamp($v)
3301 return ADOConnection::UnixTimeStamp($v);
3306 * PEAR DB Compat - do not use internally
3308 function Free()
3310 return $this->Close();
3315 * PEAR DB compat, number of rows
3317 function NumRows()
3319 return $this->_numOfRows;
3324 * PEAR DB compat, number of cols
3326 function NumCols()
3328 return $this->_numOfFields;
3332 * Fetch a row, returning false if no more rows.
3333 * This is PEAR DB compat mode.
3335 * @return false or array containing the current record
3337 function FetchRow()
3339 if ($this->EOF) {
3340 $false = false;
3341 return $false;
3343 $arr = $this->fields;
3344 $this->_currentRow++;
3345 if (!$this->_fetch()) $this->EOF = true;
3346 return $arr;
3351 * Fetch a row, returning PEAR_Error if no more rows.
3352 * This is PEAR DB compat mode.
3354 * @return DB_OK or error object
3356 function FetchInto(&$arr)
3358 if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
3359 $arr = $this->fields;
3360 $this->MoveNext();
3361 return 1; // DB_OK
3366 * Move to the first row in the recordset. Many databases do NOT support this.
3368 * @return true or false
3370 function MoveFirst()
3372 if ($this->_currentRow == 0) return true;
3373 return $this->Move(0);
3378 * Move to the last row in the recordset.
3380 * @return true or false
3382 function MoveLast()
3384 if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
3385 if ($this->EOF) return false;
3386 while (!$this->EOF) {
3387 $f = $this->fields;
3388 $this->MoveNext();
3390 $this->fields = $f;
3391 $this->EOF = false;
3392 return true;
3397 * Move to next record in the recordset.
3399 * @return true if there still rows available, or false if there are no more rows (EOF).
3401 function MoveNext()
3403 if (!$this->EOF) {
3404 $this->_currentRow++;
3405 if ($this->_fetch()) return true;
3407 $this->EOF = true;
3408 /* -- tested error handling when scrolling cursor -- seems useless.
3409 $conn = $this->connection;
3410 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
3411 $fn = $conn->raiseErrorFn;
3412 $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
3415 return false;
3420 * Random access to a specific row in the recordset. Some databases do not support
3421 * access to previous rows in the databases (no scrolling backwards).
3423 * @param rowNumber is the row to move to (0-based)
3425 * @return true if there still rows available, or false if there are no more rows (EOF).
3427 function Move($rowNumber = 0)
3429 $this->EOF = false;
3430 if ($rowNumber == $this->_currentRow) return true;
3431 if ($rowNumber >= $this->_numOfRows)
3432 if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
3434 if ($this->canSeek) {
3436 if ($this->_seek($rowNumber)) {
3437 $this->_currentRow = $rowNumber;
3438 if ($this->_fetch()) {
3439 return true;
3441 } else {
3442 $this->EOF = true;
3443 return false;
3445 } else {
3446 if ($rowNumber < $this->_currentRow) return false;
3447 global $ADODB_EXTENSION;
3448 if ($ADODB_EXTENSION) {
3449 while (!$this->EOF && $this->_currentRow < $rowNumber) {
3450 adodb_movenext($this);
3452 } else {
3454 while (! $this->EOF && $this->_currentRow < $rowNumber) {
3455 $this->_currentRow++;
3457 if (!$this->_fetch()) $this->EOF = true;
3460 return !($this->EOF);
3463 $this->fields = false;
3464 $this->EOF = true;
3465 return false;
3470 * Get the value of a field in the current row by column name.
3471 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
3473 * @param colname is the field to access
3475 * @return the value of $colname column
3477 function Fields($colname)
3479 return $this->fields[$colname];
3483 * Builds the bind array associating keys to recordset fields
3485 * @param int $upper Case for the array keys, defaults to uppercase
3486 * (see ADODB_ASSOC_CASE_xxx constants)
3488 function GetAssocKeys($upper=ADODB_ASSOC_CASE_UPPER)
3490 $this->bind = array();
3491 for ($i=0; $i < $this->_numOfFields; $i++) {
3492 $o = $this->FetchField($i);
3493 switch($upper) {
3494 case ADODB_ASSOC_CASE_LOWER:
3495 $key = strtolower($o->name);
3496 break;
3497 case ADODB_ASSOC_CASE_UPPER:
3498 $key = strtoupper($o->name);
3499 break;
3500 case ADODB_ASSOC_CASE_NATIVE:
3501 default:
3502 $key = $o->name;
3503 break;
3505 $val = $this->fetchMode == ADODB_FETCH_ASSOC ? $o->name : $i;
3506 $this->bind[$key] = $val;
3511 * Use associative array to get fields array for databases that do not support
3512 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
3514 * @param int $upper Case for the array keys, defaults to uppercase
3515 * (see ADODB_ASSOC_CASE_xxx constants)
3517 function GetRowAssoc($upper=ADODB_ASSOC_CASE_UPPER)
3519 $record = array();
3520 if (!$this->bind) {
3521 $this->GetAssocKeys($upper);
3523 foreach($this->bind as $k => $v) {
3524 if( array_key_exists( $v, $this->fields ) ) {
3525 $record[$k] = $this->fields[$v];
3526 } elseif( array_key_exists( $k, $this->fields ) ) {
3527 $record[$k] = $this->fields[$k];
3528 } else {
3529 # This should not happen... trigger error ?
3530 $record[$k] = null;
3533 return $record;
3537 * Clean up recordset
3539 * @return true or false
3541 function Close()
3543 // free connection object - this seems to globally free the object
3544 // and not merely the reference, so don't do this...
3545 // $this->connection = false;
3546 if (!$this->_closed) {
3547 $this->_closed = true;
3548 return $this->_close();
3549 } else
3550 return true;
3554 * synonyms RecordCount and RowCount
3556 * @return the number of rows or -1 if this is not supported
3558 function RecordCount() {return $this->_numOfRows;}
3562 * If we are using PageExecute(), this will return the maximum possible rows
3563 * that can be returned when paging a recordset.
3565 function MaxRecordCount()
3567 return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
3571 * synonyms RecordCount and RowCount
3573 * @return the number of rows or -1 if this is not supported
3575 function RowCount() {return $this->_numOfRows;}
3579 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
3581 * @return the number of records from a previous SELECT. All databases support this.
3583 * But aware possible problems in multiuser environments. For better speed the table
3584 * must be indexed by the condition. Heavy test this before deploying.
3586 function PO_RecordCount($table="", $condition="") {
3588 $lnumrows = $this->_numOfRows;
3589 // the database doesn't support native recordcount, so we do a workaround
3590 if ($lnumrows == -1 && $this->connection) {
3591 IF ($table) {
3592 if ($condition) $condition = " WHERE " . $condition;
3593 $resultrows = $this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
3594 if ($resultrows) $lnumrows = reset($resultrows->fields);
3597 return $lnumrows;
3602 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3604 function CurrentRow() {return $this->_currentRow;}
3607 * synonym for CurrentRow -- for ADO compat
3609 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3611 function AbsolutePosition() {return $this->_currentRow;}
3614 * @return the number of columns in the recordset. Some databases will set this to 0
3615 * if no records are returned, others will return the number of columns in the query.
3617 function FieldCount() {return $this->_numOfFields;}
3621 * Get the ADOFieldObject of a specific column.
3623 * @param fieldoffset is the column position to access(0-based).
3625 * @return the ADOFieldObject for that column, or false.
3627 function FetchField($fieldoffset = -1)
3629 // must be defined by child class
3631 $false = false;
3632 return $false;
3636 * Get the ADOFieldObjects of all columns in an array.
3639 function FieldTypesArray()
3641 $arr = array();
3642 for ($i=0, $max=$this->_numOfFields; $i < $max; $i++)
3643 $arr[] = $this->FetchField($i);
3644 return $arr;
3648 * Return the fields array of the current row as an object for convenience.
3649 * The default case is lowercase field names.
3651 * @return the object with the properties set to the fields of the current row
3653 function FetchObj()
3655 $o = $this->FetchObject(false);
3656 return $o;
3660 * Return the fields array of the current row as an object for convenience.
3661 * The default case is uppercase.
3663 * @param $isupper to set the object property names to uppercase
3665 * @return the object with the properties set to the fields of the current row
3667 function FetchObject($isupper=true)
3669 if (empty($this->_obj)) {
3670 $this->_obj = new ADOFetchObj();
3671 $this->_names = array();
3672 for ($i=0; $i <$this->_numOfFields; $i++) {
3673 $f = $this->FetchField($i);
3674 $this->_names[] = $f->name;
3677 $i = 0;
3678 if (PHP_VERSION >= 5) $o = clone($this->_obj);
3679 else $o = $this->_obj;
3681 for ($i=0; $i <$this->_numOfFields; $i++) {
3682 $name = $this->_names[$i];
3683 if ($isupper) $n = strtoupper($name);
3684 else $n = $name;
3686 $o->$n = $this->Fields($name);
3688 return $o;
3692 * Return the fields array of the current row as an object for convenience.
3693 * The default is lower-case field names.
3695 * @return the object with the properties set to the fields of the current row,
3696 * or false if EOF
3698 * Fixed bug reported by tim@orotech.net
3700 function FetchNextObj()
3702 $o = $this->FetchNextObject(false);
3703 return $o;
3708 * Return the fields array of the current row as an object for convenience.
3709 * The default is upper case field names.
3711 * @param $isupper to set the object property names to uppercase
3713 * @return the object with the properties set to the fields of the current row,
3714 * or false if EOF
3716 * Fixed bug reported by tim@orotech.net
3718 function FetchNextObject($isupper=true)
3720 $o = false;
3721 if ($this->_numOfRows != 0 && !$this->EOF) {
3722 $o = $this->FetchObject($isupper);
3723 $this->_currentRow++;
3724 if ($this->_fetch()) return $o;
3726 $this->EOF = true;
3727 return $o;
3731 * Get the metatype of the column. This is used for formatting. This is because
3732 * many databases use different names for the same type, so we transform the original
3733 * type to our standardised version which uses 1 character codes:
3735 * @param t is the type passed in. Normally is ADOFieldObject->type.
3736 * @param len is the maximum length of that field. This is because we treat character
3737 * fields bigger than a certain size as a 'B' (blob).
3738 * @param fieldobj is the field object returned by the database driver. Can hold
3739 * additional info (eg. primary_key for mysql).
3741 * @return the general type of the data:
3742 * C for character < 250 chars
3743 * X for teXt (>= 250 chars)
3744 * B for Binary
3745 * N for numeric or floating point
3746 * D for date
3747 * T for timestamp
3748 * L for logical/Boolean
3749 * I for integer
3750 * R for autoincrement counter/integer
3754 function MetaType($t,$len=-1,$fieldobj=false)
3756 if (is_object($t)) {
3757 $fieldobj = $t;
3758 $t = $fieldobj->type;
3759 $len = $fieldobj->max_length;
3761 // changed in 2.32 to hashing instead of switch stmt for speed...
3762 static $typeMap = array(
3763 'VARCHAR' => 'C',
3764 'VARCHAR2' => 'C',
3765 'CHAR' => 'C',
3766 'C' => 'C',
3767 'STRING' => 'C',
3768 'NCHAR' => 'C',
3769 'NVARCHAR' => 'C',
3770 'VARYING' => 'C',
3771 'BPCHAR' => 'C',
3772 'CHARACTER' => 'C',
3773 'INTERVAL' => 'C', # Postgres
3774 'MACADDR' => 'C', # postgres
3775 'VAR_STRING' => 'C', # mysql
3777 'LONGCHAR' => 'X',
3778 'TEXT' => 'X',
3779 'NTEXT' => 'X',
3780 'M' => 'X',
3781 'X' => 'X',
3782 'CLOB' => 'X',
3783 'NCLOB' => 'X',
3784 'LVARCHAR' => 'X',
3786 'BLOB' => 'B',
3787 'IMAGE' => 'B',
3788 'BINARY' => 'B',
3789 'VARBINARY' => 'B',
3790 'LONGBINARY' => 'B',
3791 'B' => 'B',
3793 'YEAR' => 'D', // mysql
3794 'DATE' => 'D',
3795 'D' => 'D',
3797 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
3799 'SMALLDATETIME' => 'T',
3800 'TIME' => 'T',
3801 'TIMESTAMP' => 'T',
3802 'DATETIME' => 'T',
3803 'TIMESTAMPTZ' => 'T',
3804 'T' => 'T',
3805 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
3807 'BOOL' => 'L',
3808 'BOOLEAN' => 'L',
3809 'BIT' => 'L',
3810 'L' => 'L',
3812 'COUNTER' => 'R',
3813 'R' => 'R',
3814 'SERIAL' => 'R', // ifx
3815 'INT IDENTITY' => 'R',
3817 'INT' => 'I',
3818 'INT2' => 'I',
3819 'INT4' => 'I',
3820 'INT8' => 'I',
3821 'INTEGER' => 'I',
3822 'INTEGER UNSIGNED' => 'I',
3823 'SHORT' => 'I',
3824 'TINYINT' => 'I',
3825 'SMALLINT' => 'I',
3826 'I' => 'I',
3828 'LONG' => 'N', // interbase is numeric, oci8 is blob
3829 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
3830 'DECIMAL' => 'N',
3831 'DEC' => 'N',
3832 'REAL' => 'N',
3833 'DOUBLE' => 'N',
3834 'DOUBLE PRECISION' => 'N',
3835 'SMALLFLOAT' => 'N',
3836 'FLOAT' => 'N',
3837 'NUMBER' => 'N',
3838 'NUM' => 'N',
3839 'NUMERIC' => 'N',
3840 'MONEY' => 'N',
3842 ## informix 9.2
3843 'SQLINT' => 'I',
3844 'SQLSERIAL' => 'I',
3845 'SQLSMINT' => 'I',
3846 'SQLSMFLOAT' => 'N',
3847 'SQLFLOAT' => 'N',
3848 'SQLMONEY' => 'N',
3849 'SQLDECIMAL' => 'N',
3850 'SQLDATE' => 'D',
3851 'SQLVCHAR' => 'C',
3852 'SQLCHAR' => 'C',
3853 'SQLDTIME' => 'T',
3854 'SQLINTERVAL' => 'N',
3855 'SQLBYTES' => 'B',
3856 'SQLTEXT' => 'X',
3857 ## informix 10
3858 "SQLINT8" => 'I8',
3859 "SQLSERIAL8" => 'I8',
3860 "SQLNCHAR" => 'C',
3861 "SQLNVCHAR" => 'C',
3862 "SQLLVARCHAR" => 'X',
3863 "SQLBOOL" => 'L'
3866 $tmap = false;
3867 $t = strtoupper($t);
3868 $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N';
3869 switch ($tmap) {
3870 case 'C':
3872 // is the char field is too long, return as text field...
3873 if ($this->blobSize >= 0) {
3874 if ($len > $this->blobSize) return 'X';
3875 } else if ($len > 250) {
3876 return 'X';
3878 return 'C';
3880 case 'I':
3881 if (!empty($fieldobj->primary_key)) return 'R';
3882 return 'I';
3884 case false:
3885 return 'N';
3887 case 'B':
3888 if (isset($fieldobj->binary))
3889 return ($fieldobj->binary) ? 'B' : 'X';
3890 return 'B';
3892 case 'D':
3893 if (!empty($this->connection) && !empty($this->connection->datetime)) return 'T';
3894 return 'D';
3896 default:
3897 if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B';
3898 return $tmap;
3903 function _close() {}
3906 * set/returns the current recordset page when paginating
3908 function AbsolutePage($page=-1)
3910 if ($page != -1) $this->_currentPage = $page;
3911 return $this->_currentPage;
3915 * set/returns the status of the atFirstPage flag when paginating
3917 function AtFirstPage($status=false)
3919 if ($status != false) $this->_atFirstPage = $status;
3920 return $this->_atFirstPage;
3923 function LastPageNo($page = false)
3925 if ($page != false) $this->_lastPageNo = $page;
3926 return $this->_lastPageNo;
3930 * set/returns the status of the atLastPage flag when paginating
3932 function AtLastPage($status=false)
3934 if ($status != false) $this->_atLastPage = $status;
3935 return $this->_atLastPage;
3938 } // end class ADORecordSet
3940 //==============================================================================================
3941 // CLASS ADORecordSet_array
3942 //==============================================================================================
3945 * This class encapsulates the concept of a recordset created in memory
3946 * as an array. This is useful for the creation of cached recordsets.
3948 * Note that the constructor is different from the standard ADORecordSet
3951 class ADORecordSet_array extends ADORecordSet
3953 var $databaseType = 'array';
3955 var $_array; // holds the 2-dimensional data array
3956 var $_types; // the array of types of each column (C B I L M)
3957 var $_colnames; // names of each column in array
3958 var $_skiprow1; // skip 1st row because it holds column names
3959 var $_fieldobjects; // holds array of field objects
3960 var $canSeek = true;
3961 var $affectedrows = false;
3962 var $insertid = false;
3963 var $sql = '';
3964 var $compat = false;
3966 * Constructor
3969 function ADORecordSet_array($fakeid=1)
3971 global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
3973 // fetch() on EOF does not delete $this->fields
3974 $this->compat = !empty($ADODB_COMPAT_FETCH);
3975 $this->ADORecordSet($fakeid); // fake queryID
3976 $this->fetchMode = $ADODB_FETCH_MODE;
3979 function _transpose($addfieldnames=true)
3981 global $ADODB_INCLUDED_LIB;
3983 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
3984 $hdr = true;
3986 $fobjs = $addfieldnames ? $this->_fieldobjects : false;
3987 adodb_transpose($this->_array, $newarr, $hdr, $fobjs);
3988 //adodb_pr($newarr);
3990 $this->_skiprow1 = false;
3991 $this->_array = $newarr;
3992 $this->_colnames = $hdr;
3994 adodb_probetypes($newarr,$this->_types);
3996 $this->_fieldobjects = array();
3998 foreach($hdr as $k => $name) {
3999 $f = new ADOFieldObject();
4000 $f->name = $name;
4001 $f->type = $this->_types[$k];
4002 $f->max_length = -1;
4003 $this->_fieldobjects[] = $f;
4005 $this->fields = reset($this->_array);
4007 $this->_initrs();
4012 * Setup the array.
4014 * @param array is a 2-dimensional array holding the data.
4015 * The first row should hold the column names
4016 * unless paramter $colnames is used.
4017 * @param typearr holds an array of types. These are the same types
4018 * used in MetaTypes (C,B,L,I,N).
4019 * @param [colnames] array of column names. If set, then the first row of
4020 * $array should not hold the column names.
4022 function InitArray($array,$typearr,$colnames=false)
4024 $this->_array = $array;
4025 $this->_types = $typearr;
4026 if ($colnames) {
4027 $this->_skiprow1 = false;
4028 $this->_colnames = $colnames;
4029 } else {
4030 $this->_skiprow1 = true;
4031 $this->_colnames = $array[0];
4033 $this->Init();
4036 * Setup the Array and datatype file objects
4038 * @param array is a 2-dimensional array holding the data.
4039 * The first row should hold the column names
4040 * unless paramter $colnames is used.
4041 * @param fieldarr holds an array of ADOFieldObject's.
4043 function InitArrayFields(&$array,&$fieldarr)
4045 $this->_array = $array;
4046 $this->_skiprow1= false;
4047 if ($fieldarr) {
4048 $this->_fieldobjects = $fieldarr;
4050 $this->Init();
4053 function GetArray($nRows=-1)
4055 if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
4056 return $this->_array;
4057 } else {
4058 $arr = ADORecordSet::GetArray($nRows);
4059 return $arr;
4063 function _initrs()
4065 $this->_numOfRows = sizeof($this->_array);
4066 if ($this->_skiprow1) $this->_numOfRows -= 1;
4068 $this->_numOfFields =(isset($this->_fieldobjects)) ?
4069 sizeof($this->_fieldobjects):sizeof($this->_types);
4072 /* Use associative array to get fields array */
4073 function Fields($colname)
4075 $mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
4077 if ($mode & ADODB_FETCH_ASSOC) {
4078 if (!isset($this->fields[$colname]) && !is_null($this->fields[$colname])) $colname = strtolower($colname);
4079 return $this->fields[$colname];
4081 if (!$this->bind) {
4082 $this->bind = array();
4083 for ($i=0; $i < $this->_numOfFields; $i++) {
4084 $o = $this->FetchField($i);
4085 $this->bind[strtoupper($o->name)] = $i;
4088 return $this->fields[$this->bind[strtoupper($colname)]];
4091 function FetchField($fieldOffset = -1)
4093 if (isset($this->_fieldobjects)) {
4094 return $this->_fieldobjects[$fieldOffset];
4096 $o = new ADOFieldObject();
4097 $o->name = $this->_colnames[$fieldOffset];
4098 $o->type = $this->_types[$fieldOffset];
4099 $o->max_length = -1; // length not known
4101 return $o;
4104 function _seek($row)
4106 if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
4107 $this->_currentRow = $row;
4108 if ($this->_skiprow1) $row += 1;
4109 $this->fields = $this->_array[$row];
4110 return true;
4112 return false;
4115 function MoveNext()
4117 if (!$this->EOF) {
4118 $this->_currentRow++;
4120 $pos = $this->_currentRow;
4122 if ($this->_numOfRows <= $pos) {
4123 if (!$this->compat) $this->fields = false;
4124 } else {
4125 if ($this->_skiprow1) $pos += 1;
4126 $this->fields = $this->_array[$pos];
4127 return true;
4129 $this->EOF = true;
4132 return false;
4135 function _fetch()
4137 $pos = $this->_currentRow;
4139 if ($this->_numOfRows <= $pos) {
4140 if (!$this->compat) $this->fields = false;
4141 return false;
4143 if ($this->_skiprow1) $pos += 1;
4144 $this->fields = $this->_array[$pos];
4145 return true;
4148 function _close()
4150 return true;
4153 } // ADORecordSet_array
4155 //==============================================================================================
4156 // HELPER FUNCTIONS
4157 //==============================================================================================
4160 * Synonym for ADOLoadCode. Private function. Do not use.
4162 * @deprecated
4164 function ADOLoadDB($dbType)
4166 return ADOLoadCode($dbType);
4170 * Load the code for a specific database driver. Private function. Do not use.
4172 function ADOLoadCode($dbType)
4174 global $ADODB_LASTDB;
4176 if (!$dbType) return false;
4177 $db = strtolower($dbType);
4178 switch ($db) {
4179 case 'ado':
4180 if (PHP_VERSION >= 5) $db = 'ado5';
4181 $class = 'ado';
4182 break;
4184 case 'ifx':
4185 case 'maxsql':
4186 $class = $db = 'mysqlt';
4187 break;
4189 case 'pgsql':
4190 case 'postgres':
4191 $class = $db = 'postgres8';
4192 break;
4194 default:
4195 $class = $db; break;
4198 $file = ADODB_DIR."/drivers/adodb-".$db.".inc.php";
4199 @include_once($file);
4200 $ADODB_LASTDB = $class;
4201 if (class_exists("ADODB_" . $class)) return $class;
4203 //ADOConnection::outp(adodb_pr(get_declared_classes(),true));
4204 if (!file_exists($file)) ADOConnection::outp("Missing file: $file");
4205 else ADOConnection::outp("Syntax error in file: $file");
4206 return false;
4210 * synonym for ADONewConnection for people like me who cannot remember the correct name
4212 function NewADOConnection($db='')
4214 $tmp = ADONewConnection($db);
4215 return $tmp;
4219 * Instantiate a new Connection class for a specific database driver.
4221 * @param [db] is the database Connection object to create. If undefined,
4222 * use the last database driver that was loaded by ADOLoadCode().
4224 * @return the freshly created instance of the Connection class.
4226 function ADONewConnection($db='')
4228 GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB;
4230 if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
4231 $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
4232 $false = false;
4233 if (($at = strpos($db,'://')) !== FALSE) {
4234 $origdsn = $db;
4235 $fakedsn = 'fake'.substr($origdsn,$at);
4236 if (($at2 = strpos($origdsn,'@/')) !== FALSE) {
4237 // special handling of oracle, which might not have host
4238 $fakedsn = str_replace('@/','@adodb-fakehost/',$fakedsn);
4241 if ((strpos($origdsn, 'sqlite')) !== FALSE && stripos($origdsn, '%2F') === FALSE) {
4242 // special handling for SQLite, it only might have the path to the database file.
4243 // If you try to connect to a SQLite database using a dsn
4244 // like 'sqlite:///path/to/database', the 'parse_url' php function
4245 // will throw you an exception with a message such as "unable to parse url"
4246 list($scheme, $path) = explode('://', $origdsn);
4247 $dsna['scheme'] = $scheme;
4248 if ($qmark = strpos($path,'?')) {
4249 $dsn['query'] = substr($path,$qmark+1);
4250 $path = substr($path,0,$qmark);
4252 $dsna['path'] = '/' . urlencode($path);
4253 } else
4254 $dsna = @parse_url($fakedsn);
4256 if (!$dsna) {
4257 return $false;
4259 $dsna['scheme'] = substr($origdsn,0,$at);
4260 if ($at2 !== FALSE) {
4261 $dsna['host'] = '';
4264 if (strncmp($origdsn,'pdo',3) == 0) {
4265 $sch = explode('_',$dsna['scheme']);
4266 if (sizeof($sch)>1) {
4268 $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
4269 if ($sch[1] == 'sqlite')
4270 $dsna['host'] = rawurlencode($sch[1].':'.rawurldecode($dsna['host']));
4271 else
4272 $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
4273 $dsna['scheme'] = 'pdo';
4277 $db = @$dsna['scheme'];
4278 if (!$db) return $false;
4279 $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
4280 $dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
4281 $dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
4282 $dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
4284 if (isset($dsna['query'])) {
4285 $opt1 = explode('&',$dsna['query']);
4286 foreach($opt1 as $k => $v) {
4287 $arr = explode('=',$v);
4288 $opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
4290 } else $opt = array();
4293 * phptype: Database backend used in PHP (mysql, odbc etc.)
4294 * dbsyntax: Database used with regards to SQL syntax etc.
4295 * protocol: Communication protocol to use (tcp, unix etc.)
4296 * hostspec: Host specification (hostname[:port])
4297 * database: Database to use on the DBMS server
4298 * username: User name for login
4299 * password: Password for login
4301 if (!empty($ADODB_NEWCONNECTION)) {
4302 $obj = $ADODB_NEWCONNECTION($db);
4306 if(empty($obj)) {
4308 if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
4309 if (empty($db)) $db = $ADODB_LASTDB;
4311 if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db);
4313 if (!$db) {
4314 if (isset($origdsn)) $db = $origdsn;
4315 if ($errorfn) {
4316 // raise an error
4317 $ignore = false;
4318 $errorfn('ADONewConnection', 'ADONewConnection', -998,
4319 "could not load the database driver for '$db'",
4320 $db,false,$ignore);
4321 } else
4322 ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
4324 return $false;
4327 $cls = 'ADODB_'.$db;
4328 if (!class_exists($cls)) {
4329 adodb_backtrace();
4330 return $false;
4333 $obj = new $cls();
4336 # constructor should not fail
4337 if ($obj) {
4338 if ($errorfn) $obj->raiseErrorFn = $errorfn;
4339 if (isset($dsna)) {
4340 if (isset($dsna['port'])) $obj->port = $dsna['port'];
4341 foreach($opt as $k => $v) {
4342 switch(strtolower($k)) {
4343 case 'new':
4344 $nconnect = true; $persist = true; break;
4345 case 'persist':
4346 case 'persistent': $persist = $v; break;
4347 case 'debug': $obj->debug = (integer) $v; break;
4348 #ibase
4349 case 'role': $obj->role = $v; break;
4350 case 'dialect': $obj->dialect = (integer) $v; break;
4351 case 'charset': $obj->charset = $v; $obj->charSet=$v; break;
4352 case 'buffers': $obj->buffers = $v; break;
4353 case 'fetchmode': $obj->SetFetchMode($v); break;
4354 #ado
4355 case 'charpage': $obj->charPage = $v; break;
4356 #mysql, mysqli
4357 case 'clientflags': $obj->clientFlags = $v; break;
4358 #mysql, mysqli, postgres
4359 case 'port': $obj->port = $v; break;
4360 #mysqli
4361 case 'socket': $obj->socket = $v; break;
4362 #oci8
4363 case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
4364 case 'cachesecs': $obj->cacheSecs = $v; break;
4365 case 'memcache':
4366 $varr = explode(':',$v);
4367 $vlen = sizeof($varr);
4368 if ($vlen == 0) break;
4369 $obj->memCache = true;
4370 $obj->memCacheHost = explode(',',$varr[0]);
4371 if ($vlen == 1) break;
4372 $obj->memCachePort = $varr[1];
4373 if ($vlen == 2) break;
4374 $obj->memCacheCompress = $varr[2] ? true : false;
4375 break;
4378 if (empty($persist))
4379 $ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4380 else if (empty($nconnect))
4381 $ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4382 else
4383 $ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4385 if (!$ok) return $false;
4388 return $obj;
4393 // $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
4394 function _adodb_getdriver($provider,$drivername,$perf=false)
4396 switch ($provider) {
4397 case 'odbtp': if (strncmp('odbtp_',$drivername,6)==0) return substr($drivername,6);
4398 case 'odbc' : if (strncmp('odbc_',$drivername,5)==0) return substr($drivername,5);
4399 case 'ado' : if (strncmp('ado_',$drivername,4)==0) return substr($drivername,4);
4400 case 'native': break;
4401 default:
4402 return $provider;
4405 switch($drivername) {
4406 case 'mysqlt':
4407 case 'mysqli':
4408 $drivername='mysql';
4409 break;
4410 case 'postgres7':
4411 case 'postgres8':
4412 $drivername = 'postgres';
4413 break;
4414 case 'firebird15': $drivername = 'firebird'; break;
4415 case 'oracle': $drivername = 'oci8'; break;
4416 case 'access': if ($perf) $drivername = ''; break;
4417 case 'db2' : break;
4418 case 'sapdb' : break;
4419 default:
4420 $drivername = 'generic';
4421 break;
4423 return $drivername;
4426 function NewPerfMonitor(&$conn)
4428 $false = false;
4429 $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true);
4430 if (!$drivername || $drivername == 'generic') return $false;
4431 include_once(ADODB_DIR.'/adodb-perf.inc.php');
4432 @include_once(ADODB_DIR."/perf/perf-$drivername.inc.php");
4433 $class = "Perf_$drivername";
4434 if (!class_exists($class)) return $false;
4435 $perf = new $class($conn);
4437 return $perf;
4440 function NewDataDictionary(&$conn,$drivername=false)
4442 $false = false;
4443 if (!$drivername) $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType);
4445 include_once(ADODB_DIR.'/adodb-lib.inc.php');
4446 include_once(ADODB_DIR.'/adodb-datadict.inc.php');
4447 $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php";
4449 if (!file_exists($path)) {
4450 ADOConnection::outp("Dictionary driver '$path' not available");
4451 return $false;
4453 include_once($path);
4454 $class = "ADODB2_$drivername";
4455 $dict = new $class();
4456 $dict->dataProvider = $conn->dataProvider;
4457 $dict->connection = $conn;
4458 $dict->upperName = strtoupper($drivername);
4459 $dict->quote = $conn->nameQuote;
4460 if (!empty($conn->_connectionID))
4461 $dict->serverInfo = $conn->ServerInfo();
4463 return $dict;
4469 Perform a print_r, with pre tags for better formatting.
4471 function adodb_pr($var,$as_string=false)
4473 if ($as_string) ob_start();
4475 if (isset($_SERVER['HTTP_USER_AGENT'])) {
4476 echo " <pre>\n";print_r($var);echo "</pre>\n";
4477 } else
4478 print_r($var);
4480 if ($as_string) {
4481 $s = ob_get_contents();
4482 ob_end_clean();
4483 return $s;
4488 Perform a stack-crawl and pretty print it.
4490 @param printOrArr Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
4491 @param levels Number of levels to display
4493 function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null)
4495 global $ADODB_INCLUDED_LIB;
4496 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
4497 return _adodb_backtrace($printOrArr,$levels,0,$ishtml);