019cdda9f94fd4f083d7e34fe25b31cbd621b6b4
[moodle.git] / lib / adodb / adodb.inc.php
1 <?php
2 /*
3  * Set tabs to 4 for best viewing.
4  *
5  * Latest version is available at http://adodb.sourceforge.net
6  *
7  * This is the main include file for ADOdb.
8  * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
9  *
10  * The ADOdb files are formatted so that doxygen can be used to generate documentation.
11  * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
12  */
14 /**
15         \mainpage
17         @version   v5.20.9  21-Dec-2016
18         @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
19         @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
21         Released under both BSD license and Lesser GPL library license. You can choose which license
22         you prefer.
24         PHP's database access functions are not standardised. This creates a need for a database
25         class library to hide the differences between the different database API's (encapsulate
26         the differences) so we can easily switch databases.
28         We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
29         Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
30         ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
31         other databases via ODBC.
33         Latest Download at http://adodb.sourceforge.net/
35  */
37 if (!defined('_ADODB_LAYER')) {
38         define('_ADODB_LAYER',1);
40         // The ADOdb extension is no longer maintained and effectively unsupported
41         // since v5.04. The library will not function properly if it is present.
42         if(defined('ADODB_EXTENSION')) {
43                 $msg = "Unsupported ADOdb Extension (v" . ADODB_EXTENSION . ") detected! "
44                         . "Disable it to use ADOdb";
46                 $errorfn = defined('ADODB_ERROR_HANDLER') ? ADODB_ERROR_HANDLER : false;
47                 if ($errorfn) {
48                         $conn = false;
49                         $errorfn('ADOdb', basename(__FILE__), -9999, $msg, null, null, $conn);
50                 } else {
51                         die($msg . PHP_EOL);
52                 }
53         }
55         //==============================================================================================
56         // CONSTANT DEFINITIONS
57         //==============================================================================================
60         /**
61          * Set ADODB_DIR to the directory where this file resides...
62          * This constant was formerly called $ADODB_RootPath
63          */
64         if (!defined('ADODB_DIR')) {
65                 define('ADODB_DIR',dirname(__FILE__));
66         }
68         //==============================================================================================
69         // GLOBAL VARIABLES
70         //==============================================================================================
72         GLOBAL
73                 $ADODB_vers,            // database version
74                 $ADODB_COUNTRECS,       // count number of records returned - slows down query
75                 $ADODB_CACHE_DIR,       // directory to cache recordsets
76                 $ADODB_CACHE,
77                 $ADODB_CACHE_CLASS,
78                 $ADODB_EXTENSION,   // ADODB extension installed
79                 $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
80                 $ADODB_FETCH_MODE,      // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
81                 $ADODB_GETONE_EOF,
82                 $ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.
84         //==============================================================================================
85         // GLOBAL SETUP
86         //==============================================================================================
88         $ADODB_EXTENSION = defined('ADODB_EXTENSION');
90         // ********************************************************
91         // Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
92         // Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
93         //
94         // 0 = ignore empty fields. All empty fields in array are ignored.
95         // 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
96         // 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
97         // 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.
99                 define('ADODB_FORCE_IGNORE',0);
100                 define('ADODB_FORCE_NULL',1);
101                 define('ADODB_FORCE_EMPTY',2);
102                 define('ADODB_FORCE_VALUE',3);
103         // ********************************************************
106         if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
108                 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
110         // allow [ ] @ ` " and . in table names
111                 define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
113         // prefetching used by oracle
114                 if (!defined('ADODB_PREFETCH_ROWS')) {
115                         define('ADODB_PREFETCH_ROWS',10);
116                 }
119         /**
120          * Fetch mode
121          *
122          * Set global variable $ADODB_FETCH_MODE to one of these constants or use
123          * the SetFetchMode() method to control how recordset fields are returned
124          * when fetching data.
125          *
126          *   - NUM:     array()
127          *   - ASSOC:   array('id' => 456, 'name' => 'john')
128          *   - BOTH:    array(0 => 456, 'id' => 456, 1 => 'john', 'name' => 'john')
129          *   - DEFAULT: driver-dependent
130          */
131                 define('ADODB_FETCH_DEFAULT', 0);
132                 define('ADODB_FETCH_NUM', 1);
133                 define('ADODB_FETCH_ASSOC', 2);
134                 define('ADODB_FETCH_BOTH', 3);
136         /**
137          * Associative array case constants
138          *
139          * By defining the ADODB_ASSOC_CASE constant to one of these values, it is
140          * possible to control the case of field names (associative array's keys)
141          * when operating in ADODB_FETCH_ASSOC fetch mode.
142          *   - LOWER:  $rs->fields['orderid']
143          *   - UPPER:  $rs->fields['ORDERID']
144          *   - NATIVE: $rs->fields['OrderID'] (or whatever the RDBMS will return)
145          *
146          * The default is to use native case-names.
147          *
148          * NOTE: This functionality is not implemented everywhere, it currently
149          * works only with: mssql, odbc, oci8 and ibase derived drivers
150          */
151                 define('ADODB_ASSOC_CASE_LOWER', 0);
152                 define('ADODB_ASSOC_CASE_UPPER', 1);
153                 define('ADODB_ASSOC_CASE_NATIVE', 2);
156                 if (!defined('TIMESTAMP_FIRST_YEAR')) {
157                         define('TIMESTAMP_FIRST_YEAR',100);
158                 }
160                 /**
161                  * AutoExecute constants
162                  * (moved from adodb-pear.inc.php since they are only used in here)
163                  */
164                 define('DB_AUTOQUERY_INSERT', 1);
165                 define('DB_AUTOQUERY_UPDATE', 2);
168                 // PHP's version scheme makes converting to numbers difficult - workaround
169                 $_adodb_ver = (float) PHP_VERSION;
170                 if ($_adodb_ver >= 5.2) {
171                         define('ADODB_PHPVER',0x5200);
172                 } else if ($_adodb_ver >= 5.0) {
173                         define('ADODB_PHPVER',0x5000);
174                 } else {
175                         die("PHP5 or later required. You are running ".PHP_VERSION);
176                 }
177                 unset($_adodb_ver);
178         }
181         /**
182                 Accepts $src and $dest arrays, replacing string $data
183         */
184         function ADODB_str_replace($src, $dest, $data) {
185                 if (ADODB_PHPVER >= 0x4050) {
186                         return str_replace($src,$dest,$data);
187                 }
189                 $s = reset($src);
190                 $d = reset($dest);
191                 while ($s !== false) {
192                         $data = str_replace($s,$d,$data);
193                         $s = next($src);
194                         $d = next($dest);
195                 }
196                 return $data;
197         }
199         function ADODB_Setup() {
200         GLOBAL
201                 $ADODB_vers,            // database version
202                 $ADODB_COUNTRECS,       // count number of records returned - slows down query
203                 $ADODB_CACHE_DIR,       // directory to cache recordsets
204                 $ADODB_FETCH_MODE,
205                 $ADODB_CACHE,
206                 $ADODB_CACHE_CLASS,
207                 $ADODB_FORCE_TYPE,
208                 $ADODB_GETONE_EOF,
209                 $ADODB_QUOTE_FIELDNAMES;
211                 if (empty($ADODB_CACHE_CLASS)) {
212                         $ADODB_CACHE_CLASS =  'ADODB_Cache_File' ;
213                 }
214                 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
215                 $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
216                 $ADODB_GETONE_EOF = null;
218                 if (!isset($ADODB_CACHE_DIR)) {
219                         $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
220                 } else {
221                         // do not accept url based paths, eg. http:/ or ftp:/
222                         if (strpos($ADODB_CACHE_DIR,'://') !== false) {
223                                 die("Illegal path http:// or ftp://");
224                         }
225                 }
228                 // Initialize random number generator for randomizing cache flushes
229                 // -- note Since PHP 4.2.0, the seed  becomes optional and defaults to a random value if omitted.
230                 // MDL-41198 Removed random seed initialization.
231                 // srand(((double)microtime())*1000000);
233                 /**
234                  * ADODB version as a string.
235                  */
236                 $ADODB_vers = 'v5.20.9  21-Dec-2016';
238                 /**
239                  * Determines whether recordset->RecordCount() is used.
240                  * Set to false for highest performance -- RecordCount() will always return -1 then
241                  * for databases that provide "virtual" recordcounts...
242                  */
243                 if (!isset($ADODB_COUNTRECS)) {
244                         $ADODB_COUNTRECS = true;
245                 }
246         }
249         //==============================================================================================
250         // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
251         //==============================================================================================
253         ADODB_Setup();
255         //==============================================================================================
256         // CLASS ADOFieldObject
257         //==============================================================================================
258         /**
259          * Helper class for FetchFields -- holds info on a column
260          */
261         class ADOFieldObject {
262                 var $name = '';
263                 var $max_length=0;
264                 var $type="";
265 /*
266                 // additional fields by dannym... (danny_milo@yahoo.com)
267                 var $not_null = false;
268                 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
269                 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
271                 var $has_default = false; // this one I have done only in mysql and postgres for now ...
272                         // others to come (dannym)
273                 var $default_value; // default, if any, and supported. Check has_default first.
274 */
275         }
278         function _adodb_safedate($s) {
279                 return str_replace(array("'", '\\'), '', $s);
280         }
282         // parse date string to prevent injection attack
283         // date string will have one quote at beginning e.g. '3434343'
284         function _adodb_safedateq($s) {
285                 $len = strlen($s);
286                 if ($s[0] !== "'") {
287                         $s2 = "'".$s[0];
288                 } else {
289                         $s2 = "'";
290                 }
291                 for($i=1; $i<$len; $i++) {
292                         $ch = $s[$i];
293                         if ($ch === '\\') {
294                                 $s2 .= "'";
295                                 break;
296                         } elseif ($ch === "'") {
297                                 $s2 .= $ch;
298                                 break;
299                         }
301                         $s2 .= $ch;
302                 }
304                 return strlen($s2) == 0 ? 'null' : $s2;
305         }
308         // for transaction handling
310         function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) {
311                 //print "Errorno ($fn errno=$errno m=$errmsg) ";
312                 $thisConnection->_transOK = false;
313                 if ($thisConnection->_oldRaiseFn) {
314                         $fn = $thisConnection->_oldRaiseFn;
315                         $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
316                 }
317         }
319         //------------------
320         // class for caching
321         class ADODB_Cache_File {
323                 var $createdir = true; // requires creation of temp dirs
325                 function __construct() {
326                         global $ADODB_INCLUDED_CSV;
327                         if (empty($ADODB_INCLUDED_CSV)) {
328                                 include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
329                         }
330                 }
332                 // write serialised recordset to cache item/file
333                 function writecache($filename, $contents,  $debug, $secs2cache) {
334                         return adodb_write_file($filename, $contents,$debug);
335                 }
337                 // load serialised recordset and unserialise it
338                 function &readcache($filename, &$err, $secs2cache, $rsClass) {
339                         $rs = csv2rs($filename,$err,$secs2cache,$rsClass);
340                         return $rs;
341                 }
343                 // flush all items in cache
344                 function flushall($debug=false) {
345                         global $ADODB_CACHE_DIR;
347                         $rez = false;
349                         if (strlen($ADODB_CACHE_DIR) > 1) {
350                                 $rez = $this->_dirFlush($ADODB_CACHE_DIR);
351                                 if ($debug) {
352                                         ADOConnection::outp( "flushall: $ADODB_CACHE_DIR<br><pre>\n". $rez."</pre>");
353                                 }
354                         }
355                         return $rez;
356                 }
358                 // flush one file in cache
359                 function flushcache($f, $debug=false) {
360                         if (!@unlink($f)) {
361                                 if ($debug) {
362                                         ADOConnection::outp( "flushcache: failed for $f");
363                                 }
364                         }
365                 }
367                 function getdirname($hash) {
368                         global $ADODB_CACHE_DIR;
369                         if (!isset($this->notSafeMode)) {
370                                 $this->notSafeMode = !ini_get('safe_mode');
371                         }
372                         return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
373                 }
375                 // create temp directories
376                 function createdir($hash, $debug) {
377                         global $ADODB_CACHE_PERMS;
379                         $dir = $this->getdirname($hash);
380                         if ($this->notSafeMode && !file_exists($dir)) {
381                                 $oldu = umask(0);
382                                 if (!@mkdir($dir, empty($ADODB_CACHE_PERMS) ? 0771 : $ADODB_CACHE_PERMS)) {
383                                         if(!is_dir($dir) && $debug) {
384                                                 ADOConnection::outp("Cannot create $dir");
385                                         }
386                                 }
387                                 umask($oldu);
388                         }
390                         return $dir;
391                 }
393                 /**
394                 * Private function to erase all of the files and subdirectories in a directory.
395                 *
396                 * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
397                 * Note: $kill_top_level is used internally in the function to flush subdirectories.
398                 */
399                 function _dirFlush($dir, $kill_top_level = false) {
400                         if(!$dh = @opendir($dir)) return;
402                         while (($obj = readdir($dh))) {
403                                 if($obj=='.' || $obj=='..') continue;
404                                 $f = $dir.'/'.$obj;
406                                 if (strpos($obj,'.cache')) {
407                                         @unlink($f);
408                                 }
409                                 if (is_dir($f)) {
410                                         $this->_dirFlush($f, true);
411                                 }
412                         }
413                         if ($kill_top_level === true) {
414                                 @rmdir($dir);
415                         }
416                         return true;
417                 }
418         }
420         //==============================================================================================
421         // CLASS ADOConnection
422         //==============================================================================================
424         /**
425          * Connection object. For connecting to databases, and executing queries.
426          */
427         abstract class ADOConnection {
428         //
429         // PUBLIC VARS
430         //
431         var $dataProvider = 'native';
432         var $databaseType = '';         /// RDBMS currently in use, eg. odbc, mysql, mssql
433         var $database = '';                     /// Name of database to be used.
434         var $host = '';                         /// The hostname of the database server
435         var $user = '';                         /// The username which is used to connect to the database server.
436         var $password = '';                     /// Password for the username. For security, we no longer store it.
437         var $debug = false;                     /// if set to true will output sql statements
438         var $maxblobsize = 262144;      /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
439         var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
440         var $substr = 'substr';         /// substring operator
441         var $length = 'length';         /// string length ofperator
442         var $random = 'rand()';         /// random function
443         var $upperCase = 'upper';               /// uppercase function
444         var $fmtDate = "'Y-m-d'";       /// used by DBDate() as the default date format used by the database
445         var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
446         var $true = '1';                        /// string that represents TRUE for a database
447         var $false = '0';                       /// string that represents FALSE for a database
448         var $replaceQuote = "\\'";      /// string to use to replace quotes
449         var $nameQuote = '"';           /// string to use to quote identifiers and names
450         var $charSet=false;                     /// character set to use - only for interbase, postgres and oci8
451         var $metaDatabasesSQL = '';
452         var $metaTablesSQL = '';
453         var $uniqueOrderBy = false; /// All order by columns have to be unique
454         var $emptyDate = '&nbsp;';
455         var $emptyTimeStamp = '&nbsp;';
456         var $lastInsID = false;
457         //--
458         var $hasInsertID = false;               /// supports autoincrement ID?
459         var $hasAffectedRows = false;   /// supports affected rows for update/delete?
460         var $hasTop = false;                    /// support mssql/access SELECT TOP 10 * FROM TABLE
461         var $hasLimit = false;                  /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
462         var $readOnly = false;                  /// this is a readonly database - used by phpLens
463         var $hasMoveFirst = false;              /// has ability to run MoveFirst(), scrolling backwards
464         var $hasGenID = false;                  /// can generate sequences using GenID();
465         var $hasTransactions = true;    /// has transactions
466         //--
467         var $genID = 0;                                 /// sequence id used by GenID();
468         var $raiseErrorFn = false;              /// error function to call
469         var $isoDates = false;                  /// accepts dates in ISO format
470         var $cacheSecs = 3600;                  /// cache for 1 hour
472         // memcache
473         var $memCache = false; /// should we use memCache instead of caching in files
474         var $memCacheHost; /// memCache host
475         var $memCachePort = 11211; /// memCache port
476         var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
478         var $sysDate = false; /// name of function that returns the current date
479         var $sysTimeStamp = false; /// name of function that returns the current timestamp
480         var $sysUTimeStamp = false; // name of function that returns the current timestamp accurate to the microsecond or nearest fraction
481         var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
483         var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
484         var $numCacheHits = 0;
485         var $numCacheMisses = 0;
486         var $pageExecuteCountRows = true;
487         var $uniqueSort = false; /// indicates that all fields in order by must be unique
488         var $leftOuter = false; /// operator to use for left outer join in WHERE clause
489         var $rightOuter = false; /// operator to use for right outer join in WHERE clause
490         var $ansiOuter = false; /// whether ansi outer join syntax supported
491         var $autoRollback = false; // autoRollback on PConnect().
492         var $poorAffectedRows = false; // affectedRows not working or unreliable
494         var $fnExecute = false;
495         var $fnCacheExecute = false;
496         var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
497         var $rsPrefix = "ADORecordSet_";
499         var $autoCommit = true;         /// do not modify this yourself - actually private
500         var $transOff = 0;                      /// temporarily disable transactions
501         var $transCnt = 0;                      /// count of nested transactions
503         var $fetchMode=false;
505         var $null2null = 'null'; // in autoexecute/getinsertsql/getupdatesql, this value will be converted to a null
506         var $bulkBind = false; // enable 2D Execute array
507         //
508         // PRIVATE VARS
509         //
510         var $_oldRaiseFn =  false;
511         var $_transOK = null;
512         var $_connectionID      = false;        /// The returned link identifier whenever a successful database connection is made.
513         var $_errorMsg = false;         /// A variable which was used to keep the returned last error message.  The value will
514                                                                 /// then returned by the errorMsg() function
515         var $_errorCode = false;        /// Last error code, not guaranteed to be used - only by oci8
516         var $_queryID = false;          /// This variable keeps the last created result link identifier
518         var $_isPersistentConnection = false;   /// A boolean variable to state whether its a persistent connection or normal connection.       */
519         var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
520         var $_evalAll = false;
521         var $_affected = false;
522         var $_logsql = false;
523         var $_transmode = ''; // transaction mode
525         /*
526          * Additional parameters that may be passed to drivers in the connect string
527          * Driver must be coded to accept the parameters
528          */
529         protected $connectionParameters = array();
531         /**
532         * Adds a parameter to the connection string.
533         *
534         * These parameters are added to the connection string when connecting,
535         * if the driver is coded to use it.
536         *
537         * @param        string  $parameter      The name of the parameter to set
538         * @param        string  $value          The value of the parameter
539         *
540         * @return null
541         *
542         * @example, for mssqlnative driver ('CharacterSet','UTF-8')
543         */
544         final public function setConnectionParameter($parameter,$value)
545         {
547                 $this->connectionParameters[$parameter] = $value;
549         }
551         static function Version() {
552                 global $ADODB_vers;
554                 // Semantic Version number matching regex
555                 $regex = '^[vV]?(\d+\.\d+\.\d+'         // Version number (X.Y.Z) with optional 'V'
556                         . '(?:-(?:'                         // Optional preprod version: a '-'
557                         . 'dev|'                            // followed by 'dev'
558                         . '(?:(?:alpha|beta|rc)(?:\.\d+))'  // or a preprod suffix and version number
559                         . '))?)(?:\s|$)';                   // Whitespace or end of string
561                 if (!preg_match("/$regex/", $ADODB_vers, $matches)) {
562                         // This should normally not happen... Return whatever is between the start
563                         // of the string and the first whitespace (or the end of the string).
564                         self::outp("Invalid version number: '$ADODB_vers'", 'Version');
565                         $regex = '^[vV]?(.*?)(?:\s|$)';
566                         preg_match("/$regex/", $ADODB_vers, $matches);
567                 }
568                 return $matches[1];
569         }
571         /**
572                 Get server version info...
574                 @returns An array with 2 elements: $arr['string'] is the description string,
575                         and $arr[version] is the version (also a string).
576         */
577         function ServerInfo() {
578                 return array('description' => '', 'version' => '');
579         }
581         function IsConnected() {
582                 return !empty($this->_connectionID);
583         }
585         function _findvers($str) {
586                 if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) {
587                         return $arr[1];
588                 } else {
589                         return '';
590                 }
591         }
593         /**
594         * All error messages go through this bottleneck function.
595         * You can define your own handler by defining the function name in ADODB_OUTP.
596         */
597         static function outp($msg,$newline=true) {
598                 global $ADODB_FLUSH,$ADODB_OUTP;
600                 if (defined('ADODB_OUTP')) {
601                         $fn = ADODB_OUTP;
602                         $fn($msg,$newline);
603                         return;
604                 } else if (isset($ADODB_OUTP)) {
605                         $fn = $ADODB_OUTP;
606                         $fn($msg,$newline);
607                         return;
608                 }
610                 if ($newline) {
611                         $msg .= "<br>\n";
612                 }
614                 if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) {
615                         echo $msg;
616                 } else {
617                         echo strip_tags($msg);
618                 }
621                 if (!empty($ADODB_FLUSH) && ob_get_length() !== false) {
622                         flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan
623                 }
625         }
627         function Time() {
628                 $rs = $this->_Execute("select $this->sysTimeStamp");
629                 if ($rs && !$rs->EOF) {
630                         return $this->UnixTimeStamp(reset($rs->fields));
631                 }
633                 return false;
634         }
636         /**
637          * Connect to database
638          *
639          * @param [argHostname]         Host to connect to
640          * @param [argUsername]         Userid to login
641          * @param [argPassword]         Associated password
642          * @param [argDatabaseName]     database
643          * @param [forceNew]            force new connection
644          *
645          * @return true or false
646          */
647         function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) {
648                 if ($argHostname != "") {
649                         $this->host = $argHostname;
650                 }
651                 if ( strpos($this->host, ':') > 0 && isset($this->port) ) {
652                         list($this->host, $this->port) = explode(":", $this->host, 2);
653                 }
654                 if ($argUsername != "") {
655                         $this->user = $argUsername;
656                 }
657                 if ($argPassword != "") {
658                         $this->password = 'not stored'; // not stored for security reasons
659                 }
660                 if ($argDatabaseName != "") {
661                         $this->database = $argDatabaseName;
662                 }
664                 $this->_isPersistentConnection = false;
666                 if ($forceNew) {
667                         if ($rez=$this->_nconnect($this->host, $this->user, $argPassword, $this->database)) {
668                                 return true;
669                         }
670                 } else {
671                         if ($rez=$this->_connect($this->host, $this->user, $argPassword, $this->database)) {
672                                 return true;
673                         }
674                 }
675                 if (isset($rez)) {
676                         $err = $this->ErrorMsg();
677                         $errno = $this->ErrorNo();
678                         if (empty($err)) {
679                                 $err = "Connection error to server '$argHostname' with user '$argUsername'";
680                         }
681                 } else {
682                         $err = "Missing extension for ".$this->dataProvider;
683                         $errno = 0;
684                 }
685                 if ($fn = $this->raiseErrorFn) {
686                         $fn($this->databaseType, 'CONNECT', $errno, $err, $this->host, $this->database, $this);
687                 }
689                 $this->_connectionID = false;
690                 if ($this->debug) {
691                         ADOConnection::outp( $this->host.': '.$err);
692                 }
693                 return false;
694         }
696         function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) {
697                 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
698         }
701         /**
702          * Always force a new connection to database - currently only works with oracle
703          *
704          * @param [argHostname]         Host to connect to
705          * @param [argUsername]         Userid to login
706          * @param [argPassword]         Associated password
707          * @param [argDatabaseName]     database
708          *
709          * @return true or false
710          */
711         function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
712                 return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
713         }
715         /**
716          * Establish persistent connect to database
717          *
718          * @param [argHostname]         Host to connect to
719          * @param [argUsername]         Userid to login
720          * @param [argPassword]         Associated password
721          * @param [argDatabaseName]     database
722          *
723          * @return return true or false
724          */
725         function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
727                 if (defined('ADODB_NEVER_PERSIST')) {
728                         return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
729                 }
731                 if ($argHostname != "") {
732                         $this->host = $argHostname;
733                 }
734                 if ( strpos($this->host, ':') > 0 && isset($this->port) ) {
735                         list($this->host, $this->port) = explode(":", $this->host, 2);
736                 }
737                 if ($argUsername != "") {
738                         $this->user = $argUsername;
739                 }
740                 if ($argPassword != "") {
741                         $this->password = 'not stored';
742                 }
743                 if ($argDatabaseName != "") {
744                         $this->database = $argDatabaseName;
745                 }
747                 $this->_isPersistentConnection = true;
749                 if ($rez = $this->_pconnect($this->host, $this->user, $argPassword, $this->database)) {
750                         return true;
751                 }
752                 if (isset($rez)) {
753                         $err = $this->ErrorMsg();
754                         if (empty($err)) {
755                                 $err = "Connection error to server '$argHostname' with user '$argUsername'";
756                         }
757                         $ret = false;
758                 } else {
759                         $err = "Missing extension for ".$this->dataProvider;
760                         $ret = 0;
761                 }
762                 if ($fn = $this->raiseErrorFn) {
763                         $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
764                 }
766                 $this->_connectionID = false;
767                 if ($this->debug) {
768                         ADOConnection::outp( $this->host.': '.$err);
769                 }
770                 return $ret;
771         }
773         function outp_throw($msg,$src='WARN',$sql='') {
774                 if (defined('ADODB_ERROR_HANDLER') &&  ADODB_ERROR_HANDLER == 'adodb_throw') {
775                         adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
776                         return;
777                 }
778                 ADOConnection::outp($msg);
779         }
781         // create cache class. Code is backward compat with old memcache implementation
782         function _CreateCache() {
783                 global $ADODB_CACHE, $ADODB_CACHE_CLASS;
785                 if ($this->memCache) {
786                         global $ADODB_INCLUDED_MEMCACHE;
788                         if (empty($ADODB_INCLUDED_MEMCACHE)) {
789                                 include_once(ADODB_DIR.'/adodb-memcache.lib.inc.php');
790                         }
791                         $ADODB_CACHE = new ADODB_Cache_MemCache($this);
792                 } else {
793                         $ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
794                 }
795         }
797         // Format date column in sql string given an input format that understands Y M D
798         function SQLDate($fmt, $col=false) {
799                 if (!$col) {
800                         $col = $this->sysDate;
801                 }
802                 return $col; // child class implement
803         }
805         /**
806          * Should prepare the sql statement and return the stmt resource.
807          * For databases that do not support this, we return the $sql. To ensure
808          * compatibility with databases that do not support prepare:
809          *
810          *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
811          *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
812          *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
813          *
814          * @param sql   SQL to send to database
815          *
816          * @return return FALSE, or the prepared statement, or the original sql if
817          *         if the database does not support prepare.
818          *
819          */
820         function Prepare($sql) {
821                 return $sql;
822         }
824         /**
825          * Some databases, eg. mssql require a different function for preparing
826          * stored procedures. So we cannot use Prepare().
827          *
828          * Should prepare the stored procedure  and return the stmt resource.
829          * For databases that do not support this, we return the $sql. To ensure
830          * compatibility with databases that do not support prepare:
831          *
832          * @param sql   SQL to send to database
833          *
834          * @return return FALSE, or the prepared statement, or the original sql if
835          *         if the database does not support prepare.
836          *
837          */
838         function PrepareSP($sql,$param=true) {
839                 return $this->Prepare($sql,$param);
840         }
842         /**
843         * PEAR DB Compat
844         */
845         function Quote($s) {
846                 return $this->qstr($s,false);
847         }
849         /**
850          * Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
851          */
852         function QMagic($s) {
853                 return $this->qstr($s,get_magic_quotes_gpc());
854         }
856         function q(&$s) {
857                 //if (!empty($this->qNull && $s == 'null') {
858                 //      return $s;
859                 //}
860                 $s = $this->qstr($s,false);
861         }
863         /**
864         * PEAR DB Compat - do not use internally.
865         */
866         function ErrorNative() {
867                 return $this->ErrorNo();
868         }
871         /**
872          * PEAR DB Compat - do not use internally.
873          */
874         function nextId($seq_name) {
875                 return $this->GenID($seq_name);
876         }
878         /**
879          * Lock a row, will escalate and lock the table if row locking not supported
880          * will normally free the lock at the end of the transaction
881          *
882          * @param $table        name of table to lock
883          * @param $where        where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
884          */
885         function RowLock($table,$where,$col='1 as adodbignore') {
886                 return false;
887         }
889         function CommitLock($table) {
890                 return $this->CommitTrans();
891         }
893         function RollbackLock($table) {
894                 return $this->RollbackTrans();
895         }
897         /**
898         * PEAR DB Compat - do not use internally.
899         *
900         * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
901         * for easy porting :-)
902         *
903         * @param mode   The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
904         * @returns              The previous fetch mode
905         */
906         function SetFetchMode($mode) {
907                 $old = $this->fetchMode;
908                 $this->fetchMode = $mode;
910                 if ($old === false) {
911                         global $ADODB_FETCH_MODE;
912                         return $ADODB_FETCH_MODE;
913                 }
914                 return $old;
915         }
918         /**
919         * PEAR DB Compat - do not use internally.
920         */
921         function Query($sql, $inputarr=false) {
922                 $rs = $this->Execute($sql, $inputarr);
923                 if (!$rs && defined('ADODB_PEAR')) {
924                         return ADODB_PEAR_Error();
925                 }
926                 return $rs;
927         }
930         /**
931         * PEAR DB Compat - do not use internally
932         */
933         function LimitQuery($sql, $offset, $count, $params=false) {
934                 $rs = $this->SelectLimit($sql, $count, $offset, $params);
935                 if (!$rs && defined('ADODB_PEAR')) {
936                         return ADODB_PEAR_Error();
937                 }
938                 return $rs;
939         }
942         /**
943         * PEAR DB Compat - do not use internally
944         */
945         function Disconnect() {
946                 return $this->Close();
947         }
949         /**
950          * Returns a placeholder for query parameters
951          * e.g. $DB->Param('a') will return
952          * - '?' for most databases
953          * - ':a' for Oracle
954          * - '$1', '$2', etc. for PostgreSQL
955          * @param string $name parameter's name, false to force a reset of the
956          *                     number to 1 (for databases that require positioned
957          *                     params such as PostgreSQL; note that ADOdb will
958          *                     automatically reset this when executing a query )
959          * @param string $type (unused)
960          * @return string query parameter placeholder
961          */
962         function Param($name,$type='C') {
963                 return '?';
964         }
966         /*
967                 InParameter and OutParameter are self-documenting versions of Parameter().
968         */
969         function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
970                 return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
971         }
973         /*
974         */
975         function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
976                 return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
978         }
981         /*
982         Usage in oracle
983                 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
984                 $db->Parameter($stmt,$id,'myid');
985                 $db->Parameter($stmt,$group,'group',64);
986                 $db->Execute();
988                 @param $stmt Statement returned by Prepare() or PrepareSP().
989                 @param $var PHP variable to bind to
990                 @param $name Name of stored procedure variable name to bind to.
991                 @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
992                 @param [$maxLen] Holds an maximum length of the variable.
993                 @param [$type] The data type of $var. Legal values depend on driver.
995         */
996         function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) {
997                 return false;
998         }
1001         function IgnoreErrors($saveErrs=false) {
1002                 if (!$saveErrs) {
1003                         $saveErrs = array($this->raiseErrorFn,$this->_transOK);
1004                         $this->raiseErrorFn = false;
1005                         return $saveErrs;
1006                 } else {
1007                         $this->raiseErrorFn = $saveErrs[0];
1008                         $this->_transOK = $saveErrs[1];
1009                 }
1010         }
1012         /**
1013          * Improved method of initiating a transaction. Used together with CompleteTrans().
1014          * Advantages include:
1015      *
1016          * a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
1017          *    Only the outermost block is treated as a transaction.<br>
1018          * b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
1019          * c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
1020          *    are disabled, making it backward compatible.
1021          */
1022         function StartTrans($errfn = 'ADODB_TransMonitor') {
1023                 if ($this->transOff > 0) {
1024                         $this->transOff += 1;
1025                         return true;
1026                 }
1028                 $this->_oldRaiseFn = $this->raiseErrorFn;
1029                 $this->raiseErrorFn = $errfn;
1030                 $this->_transOK = true;
1032                 if ($this->debug && $this->transCnt > 0) {
1033                         ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
1034                 }
1035                 $ok = $this->BeginTrans();
1036                 $this->transOff = 1;
1037                 return $ok;
1038         }
1041         /**
1042                 Used together with StartTrans() to end a transaction. Monitors connection
1043                 for sql errors, and will commit or rollback as appropriate.
1045                 @autoComplete if true, monitor sql errors and commit and rollback as appropriate,
1046                 and if set to false force rollback even if no SQL error detected.
1047                 @returns true on commit, false on rollback.
1048         */
1049         function CompleteTrans($autoComplete = true) {
1050                 if ($this->transOff > 1) {
1051                         $this->transOff -= 1;
1052                         return true;
1053                 }
1054                 $this->raiseErrorFn = $this->_oldRaiseFn;
1056                 $this->transOff = 0;
1057                 if ($this->_transOK && $autoComplete) {
1058                         if (!$this->CommitTrans()) {
1059                                 $this->_transOK = false;
1060                                 if ($this->debug) {
1061                                         ADOConnection::outp("Smart Commit failed");
1062                                 }
1063                         } else {
1064                                 if ($this->debug) {
1065                                         ADOConnection::outp("Smart Commit occurred");
1066                                 }
1067                         }
1068                 } else {
1069                         $this->_transOK = false;
1070                         $this->RollbackTrans();
1071                         if ($this->debug) {
1072                                 ADOCOnnection::outp("Smart Rollback occurred");
1073                         }
1074                 }
1076                 return $this->_transOK;
1077         }
1079         /*
1080                 At the end of a StartTrans/CompleteTrans block, perform a rollback.
1081         */
1082         function FailTrans() {
1083                 if ($this->debug)
1084                         if ($this->transOff == 0) {
1085                                 ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
1086                         } else {
1087                                 ADOConnection::outp("FailTrans was called");
1088                                 adodb_backtrace();
1089                         }
1090                 $this->_transOK = false;
1091         }
1093         /**
1094                 Check if transaction has failed, only for Smart Transactions.
1095         */
1096         function HasFailedTrans() {
1097                 if ($this->transOff > 0) {
1098                         return $this->_transOK == false;
1099                 }
1100                 return false;
1101         }
1103         /**
1104          * Execute SQL
1105          *
1106          * @param sql           SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
1107          * @param [inputarr]    holds the input data to bind to. Null elements will be set to null.
1108          * @return RecordSet or false
1109          */
1110         function Execute($sql,$inputarr=false) {
1111                 if ($this->fnExecute) {
1112                         $fn = $this->fnExecute;
1113                         $ret = $fn($this,$sql,$inputarr);
1114                         if (isset($ret)) {
1115                                 return $ret;
1116                         }
1117                 }
1118                 if ($inputarr !== false) {
1119                         if (!is_array($inputarr)) {
1120                                 $inputarr = array($inputarr);
1121                         }
1123                         $element0 = reset($inputarr);
1124                         # is_object check because oci8 descriptors can be passed in
1125                         $array_2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
1127                         //remove extra memory copy of input -mikefedyk
1128                         unset($element0);
1130                         if (!is_array($sql) && !$this->_bindInputArray) {
1131                                 // @TODO this would consider a '?' within a string as a parameter...
1132                                 $sqlarr = explode('?',$sql);
1133                                 $nparams = sizeof($sqlarr)-1;
1135                                 if (!$array_2d) {
1136                                         // When not Bind Bulk - convert to array of arguments list
1137                                         $inputarr = array($inputarr);
1138                                 } else {
1139                                         // Bulk bind - Make sure all list of params have the same number of elements
1140                                         $countElements = array_map('count', $inputarr);
1141                                         if (1 != count(array_unique($countElements))) {
1142                                                 $this->outp_throw(
1143                                                         "[bulk execute] Input array has different number of params  [" . print_r($countElements, true) .  "].",
1144                                                         'Execute'
1145                                                 );
1146                                                 return false;
1147                                         }
1148                                         unset($countElements);
1149                                 }
1150                                 // Make sure the number of parameters provided in the input
1151                                 // array matches what the query expects
1152                                 $element0 = reset($inputarr);
1153                                 if ($nparams != count($element0)) {
1154                                         $this->outp_throw(
1155                                                 "Input array has " . count($element0) .
1156                                                 " params, does not match query: '" . htmlspecialchars($sql) . "'",
1157                                                 'Execute'
1158                                         );
1159                                         return false;
1160                                 }
1162                                 // clean memory
1163                                 unset($element0);
1165                                 foreach($inputarr as $arr) {
1166                                         $sql = ''; $i = 0;
1167                                         //Use each() instead of foreach to reduce memory usage -mikefedyk
1168                                         while(list(, $v) = each($arr)) {
1169                                                 $sql .= $sqlarr[$i];
1170                                                 // from Ron Baldwin <ron.baldwin#sourceprose.com>
1171                                                 // Only quote string types
1172                                                 $typ = gettype($v);
1173                                                 if ($typ == 'string') {
1174                                                         //New memory copy of input created here -mikefedyk
1175                                                         $sql .= $this->qstr($v);
1176                                                 } else if ($typ == 'double') {
1177                                                         $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
1178                                                 } else if ($typ == 'boolean') {
1179                                                         $sql .= $v ? $this->true : $this->false;
1180                                                 } else if ($typ == 'object') {
1181                                                         if (method_exists($v, '__toString')) {
1182                                                                 $sql .= $this->qstr($v->__toString());
1183                                                         } else {
1184                                                                 $sql .= $this->qstr((string) $v);
1185                                                         }
1186                                                 } else if ($v === null) {
1187                                                         $sql .= 'NULL';
1188                                                 } else {
1189                                                         $sql .= $v;
1190                                                 }
1191                                                 $i += 1;
1193                                                 if ($i == $nparams) {
1194                                                         break;
1195                                                 }
1196                                         } // while
1197                                         if (isset($sqlarr[$i])) {
1198                                                 $sql .= $sqlarr[$i];
1199                                                 if ($i+1 != sizeof($sqlarr)) {
1200                                                         $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
1201                                                 }
1202                                         } else if ($i != sizeof($sqlarr)) {
1203                                                 $this->outp_throw( "Input array does not match ?: ".htmlspecialchars($sql),'Execute');
1204                                         }
1206                                         $ret = $this->_Execute($sql);
1207                                         if (!$ret) {
1208                                                 return $ret;
1209                                         }
1210                                 }
1211                         } else {
1212                                 if ($array_2d) {
1213                                         if (is_string($sql)) {
1214                                                 $stmt = $this->Prepare($sql);
1215                                         } else {
1216                                                 $stmt = $sql;
1217                                         }
1219                                         foreach($inputarr as $arr) {
1220                                                 $ret = $this->_Execute($stmt,$arr);
1221                                                 if (!$ret) {
1222                                                         return $ret;
1223                                                 }
1224                                         }
1225                                 } else {
1226                                         $ret = $this->_Execute($sql,$inputarr);
1227                                 }
1228                         }
1229                 } else {
1230                         $ret = $this->_Execute($sql,false);
1231                 }
1233                 return $ret;
1234         }
1236         function _Execute($sql,$inputarr=false) {
1237                 // ExecuteCursor() may send non-string queries (such as arrays),
1238                 // so we need to ignore those.
1239                 if( is_string($sql) ) {
1240                         // Strips keyword used to help generate SELECT COUNT(*) queries
1241                         // from SQL if it exists.
1242                         $sql = ADODB_str_replace( '_ADODB_COUNT', '', $sql );
1243                 }
1245                 if ($this->debug) {
1246                         global $ADODB_INCLUDED_LIB;
1247                         if (empty($ADODB_INCLUDED_LIB)) {
1248                                 include(ADODB_DIR.'/adodb-lib.inc.php');
1249                         }
1250                         $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
1251                 } else {
1252                         $this->_queryID = @$this->_query($sql,$inputarr);
1253                 }
1255                 // ************************
1256                 // OK, query executed
1257                 // ************************
1259                 // error handling if query fails
1260                 if ($this->_queryID === false) {
1261                         if ($this->debug == 99) {
1262                                 adodb_backtrace(true,5);
1263                         }
1264                         $fn = $this->raiseErrorFn;
1265                         if ($fn) {
1266                                 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
1267                         }
1268                         return false;
1269                 }
1271                 // return simplified recordset for inserts/updates/deletes with lower overhead
1272                 if ($this->_queryID === true) {
1273                         $rsclass = $this->rsPrefix.'empty';
1274                         $rs = (class_exists($rsclass)) ? new $rsclass():  new ADORecordSet_empty();
1276                         return $rs;
1277                 }
1279                 // return real recordset from select statement
1280                 $rsclass = $this->rsPrefix.$this->databaseType;
1281                 $rs = new $rsclass($this->_queryID,$this->fetchMode);
1282                 $rs->connection = $this; // Pablo suggestion
1283                 $rs->Init();
1284                 if (is_array($sql)) {
1285                         $rs->sql = $sql[0];
1286                 } else {
1287                         $rs->sql = $sql;
1288                 }
1289                 if ($rs->_numOfRows <= 0) {
1290                         global $ADODB_COUNTRECS;
1291                         if ($ADODB_COUNTRECS) {
1292                                 if (!$rs->EOF) {
1293                                         $rs = $this->_rs2rs($rs,-1,-1,!is_array($sql));
1294                                         $rs->_queryID = $this->_queryID;
1295                                 } else
1296                                         $rs->_numOfRows = 0;
1297                         }
1298                 }
1299                 return $rs;
1300         }
1302         function CreateSequence($seqname='adodbseq',$startID=1) {
1303                 if (empty($this->_genSeqSQL)) {
1304                         return false;
1305                 }
1306                 return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1307         }
1309         function DropSequence($seqname='adodbseq') {
1310                 if (empty($this->_dropSeqSQL)) {
1311                         return false;
1312                 }
1313                 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
1314         }
1316         /**
1317          * Generates a sequence id and stores it in $this->genID;
1318          * GenID is only available if $this->hasGenID = true;
1319          *
1320          * @param seqname               name of sequence to use
1321          * @param startID               if sequence does not exist, start at this ID
1322          * @return              0 if not supported, otherwise a sequence id
1323          */
1324         function GenID($seqname='adodbseq',$startID=1) {
1325                 if (!$this->hasGenID) {
1326                         return 0; // formerly returns false pre 1.60
1327                 }
1329                 $getnext = sprintf($this->_genIDSQL,$seqname);
1331                 $holdtransOK = $this->_transOK;
1333                 $save_handler = $this->raiseErrorFn;
1334                 $this->raiseErrorFn = '';
1335                 @($rs = $this->Execute($getnext));
1336                 $this->raiseErrorFn = $save_handler;
1338                 if (!$rs) {
1339                         $this->_transOK = $holdtransOK; //if the status was ok before reset
1340                         $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1341                         $rs = $this->Execute($getnext);
1342                 }
1343                 if ($rs && !$rs->EOF) {
1344                         $this->genID = reset($rs->fields);
1345                 } else {
1346                         $this->genID = 0; // false
1347                 }
1349                 if ($rs) {
1350                         $rs->Close();
1351                 }
1353                 return $this->genID;
1354         }
1356         /**
1357          * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
1358          * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
1359          * @return  the last inserted ID. Not all databases support this.
1360          */
1361         function Insert_ID($table='',$column='') {
1362                 if ($this->_logsql && $this->lastInsID) {
1363                         return $this->lastInsID;
1364                 }
1365                 if ($this->hasInsertID) {
1366                         return $this->_insertid($table,$column);
1367                 }
1368                 if ($this->debug) {
1369                         ADOConnection::outp( '<p>Insert_ID error</p>');
1370                         adodb_backtrace();
1371                 }
1372                 return false;
1373         }
1376         /**
1377          * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
1378          *
1379          * @return  the last inserted ID. All databases support this. But aware possible
1380          * problems in multiuser environments. Heavy test this before deploying.
1381          */
1382         function PO_Insert_ID($table="", $id="") {
1383                 if ($this->hasInsertID){
1384                         return $this->Insert_ID($table,$id);
1385                 } else {
1386                         return $this->GetOne("SELECT MAX($id) FROM $table");
1387                 }
1388         }
1390         /**
1391         * @return # rows affected by UPDATE/DELETE
1392         */
1393         function Affected_Rows() {
1394                 if ($this->hasAffectedRows) {
1395                         if ($this->fnExecute === 'adodb_log_sql') {
1396                                 if ($this->_logsql && $this->_affected !== false) {
1397                                         return $this->_affected;
1398                                 }
1399                         }
1400                         $val = $this->_affectedrows();
1401                         return ($val < 0) ? false : $val;
1402                 }
1404                 if ($this->debug) {
1405                         ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1406                 }
1407                 return false;
1408         }
1411         /**
1412          * @return  the last error message
1413          */
1414         function ErrorMsg() {
1415                 if ($this->_errorMsg) {
1416                         return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1417                 } else {
1418                         return '';
1419                 }
1420         }
1423         /**
1424          * @return the last error number. Normally 0 means no error.
1425          */
1426         function ErrorNo() {
1427                 return ($this->_errorMsg) ? -1 : 0;
1428         }
1430         function MetaError($err=false) {
1431                 include_once(ADODB_DIR."/adodb-error.inc.php");
1432                 if ($err === false) {
1433                         $err = $this->ErrorNo();
1434                 }
1435                 return adodb_error($this->dataProvider,$this->databaseType,$err);
1436         }
1438         function MetaErrorMsg($errno) {
1439                 include_once(ADODB_DIR."/adodb-error.inc.php");
1440                 return adodb_errormsg($errno);
1441         }
1443         /**
1444          * @returns an array with the primary key columns in it.
1445          */
1446         function MetaPrimaryKeys($table, $owner=false) {
1447         // owner not used in base class - see oci8
1448                 $p = array();
1449                 $objs = $this->MetaColumns($table);
1450                 if ($objs) {
1451                         foreach($objs as $v) {
1452                                 if (!empty($v->primary_key)) {
1453                                         $p[] = $v->name;
1454                                 }
1455                         }
1456                 }
1457                 if (sizeof($p)) {
1458                         return $p;
1459                 }
1460                 if (function_exists('ADODB_VIEW_PRIMARYKEYS')) {
1461                         return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1462                 }
1463                 return false;
1464         }
1466         /**
1467          * @returns assoc array where keys are tables, and values are foreign keys
1468          */
1469         function MetaForeignKeys($table, $owner=false, $upper=false) {
1470                 return false;
1471         }
1472         /**
1473          * Choose a database to connect to. Many databases do not support this.
1474          *
1475          * @param dbName is the name of the database to select
1476          * @return true or false
1477          */
1478         function SelectDB($dbName) {return false;}
1481         /**
1482          * Will select, getting rows from $offset (1-based), for $nrows.
1483          * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1484          * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1485          * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1486          * eg.
1487          *  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1488          *  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1489          *
1490          * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1491          * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1492          *
1493          * @param sql
1494          * @param [offset]      is the row to start calculations from (1-based)
1495          * @param [nrows]               is the number of rows to get
1496          * @param [inputarr]    array of bind variables
1497          * @param [secs2cache]          is a private parameter only used by jlim
1498          * @return              the recordset ($rs->databaseType == 'array')
1499          */
1500         function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) {
1501                 if ($this->hasTop && $nrows > 0) {
1502                         // suggested by Reinhard Balling. Access requires top after distinct
1503                         // Informix requires first before distinct - F Riosa
1504                         $ismssql = (strpos($this->databaseType,'mssql') !== false);
1505                         if ($ismssql) {
1506                                 $isaccess = false;
1507                         } else {
1508                                 $isaccess = (strpos($this->databaseType,'access') !== false);
1509                         }
1511                         if ($offset <= 0) {
1512                                         // access includes ties in result
1513                                         if ($isaccess) {
1514                                                 $sql = preg_replace(
1515                                                 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1517                                                 if ($secs2cache != 0) {
1518                                                         $ret = $this->CacheExecute($secs2cache, $sql,$inputarr);
1519                                                 } else {
1520                                                         $ret = $this->Execute($sql,$inputarr);
1521                                                 }
1522                                                 return $ret; // PHP5 fix
1523                                         } else if ($ismssql){
1524                                                 $sql = preg_replace(
1525                                                 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1526                                         } else {
1527                                                 $sql = preg_replace(
1528                                                 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1529                                         }
1530                         } else {
1531                                 $nn = $nrows + $offset;
1532                                 if ($isaccess || $ismssql) {
1533                                         $sql = preg_replace(
1534                                         '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1535                                 } else {
1536                                         $sql = preg_replace(
1537                                         '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1538                                 }
1539                         }
1540                 }
1542                 // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
1543                 // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1544                 global $ADODB_COUNTRECS;
1546                 $savec = $ADODB_COUNTRECS;
1547                 $ADODB_COUNTRECS = false;
1550                 if ($secs2cache != 0) {
1551                         $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1552                 } else {
1553                         $rs = $this->Execute($sql,$inputarr);
1554                 }
1556                 $ADODB_COUNTRECS = $savec;
1557                 if ($rs && !$rs->EOF) {
1558                         $rs = $this->_rs2rs($rs,$nrows,$offset);
1559                 }
1560                 //print_r($rs);
1561                 return $rs;
1562         }
1564         /**
1565         * Create serializable recordset. Breaks rs link to connection.
1566         *
1567         * @param rs                     the recordset to serialize
1568         */
1569         function SerializableRS(&$rs) {
1570                 $rs2 = $this->_rs2rs($rs);
1571                 $ignore = false;
1572                 $rs2->connection = $ignore;
1574                 return $rs2;
1575         }
1577         /**
1578         * Convert database recordset to an array recordset
1579         * input recordset's cursor should be at beginning, and
1580         * old $rs will be closed.
1581         *
1582         * @param rs                     the recordset to copy
1583         * @param [nrows]        number of rows to retrieve (optional)
1584         * @param [offset]       offset by number of rows (optional)
1585         * @return                       the new recordset
1586         */
1587         function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) {
1588                 if (! $rs) {
1589                         return false;
1590                 }
1591                 $dbtype = $rs->databaseType;
1592                 if (!$dbtype) {
1593                         $rs = $rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1594                         return $rs;
1595                 }
1596                 if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1597                         $rs->MoveFirst();
1598                         $rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1599                         return $rs;
1600                 }
1601                 $flds = array();
1602                 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1603                         $flds[] = $rs->FetchField($i);
1604                 }
1606                 $arr = $rs->GetArrayLimit($nrows,$offset);
1607                 //print_r($arr);
1608                 if ($close) {
1609                         $rs->Close();
1610                 }
1612                 $arrayClass = $this->arrayClass;
1614                 $rs2 = new $arrayClass();
1615                 $rs2->connection = $this;
1616                 $rs2->sql = $rs->sql;
1617                 $rs2->dataProvider = $this->dataProvider;
1618                 $rs2->InitArrayFields($arr,$flds);
1619                 $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1620                 return $rs2;
1621         }
1623         /*
1624         * Return all rows. Compat with PEAR DB
1625         */
1626         function GetAll($sql, $inputarr=false) {
1627                 $arr = $this->GetArray($sql,$inputarr);
1628                 return $arr;
1629         }
1631         function GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false) {
1632                 $rs = $this->Execute($sql, $inputarr);
1633                 if (!$rs) {
1634                         return false;
1635                 }
1636                 $arr = $rs->GetAssoc($force_array,$first2cols);
1637                 return $arr;
1638         }
1640         function CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false) {
1641                 if (!is_numeric($secs2cache)) {
1642                         $first2cols = $force_array;
1643                         $force_array = $inputarr;
1644                 }
1645                 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
1646                 if (!$rs) {
1647                         return false;
1648                 }
1649                 $arr = $rs->GetAssoc($force_array,$first2cols);
1650                 return $arr;
1651         }
1653         /**
1654         * Return first element of first row of sql statement. Recordset is disposed
1655         * for you.
1656         *
1657         * @param sql                    SQL statement
1658         * @param [inputarr]             input bind array
1659         */
1660         function GetOne($sql,$inputarr=false) {
1661                 global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
1663                 $crecs = $ADODB_COUNTRECS;
1664                 $ADODB_COUNTRECS = false;
1666                 $ret = false;
1667                 $rs = $this->Execute($sql,$inputarr);
1668                 if ($rs) {
1669                         if ($rs->EOF) {
1670                                 $ret = $ADODB_GETONE_EOF;
1671                         } else {
1672                                 $ret = reset($rs->fields);
1673                         }
1675                         $rs->Close();
1676                 }
1677                 $ADODB_COUNTRECS = $crecs;
1678                 return $ret;
1679         }
1681         // $where should include 'WHERE fld=value'
1682         function GetMedian($table, $field,$where = '') {
1683                 $total = $this->GetOne("select count(*) from $table $where");
1684                 if (!$total) {
1685                         return false;
1686                 }
1688                 $midrow = (integer) ($total/2);
1689                 $rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
1690                 if ($rs && !$rs->EOF) {
1691                         return reset($rs->fields);
1692                 }
1693                 return false;
1694         }
1697         function CacheGetOne($secs2cache,$sql=false,$inputarr=false) {
1698                 global $ADODB_GETONE_EOF;
1700                 $ret = false;
1701                 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1702                 if ($rs) {
1703                         if ($rs->EOF) {
1704                                 $ret = $ADODB_GETONE_EOF;
1705                         } else {
1706                                 $ret = reset($rs->fields);
1707                         }
1708                         $rs->Close();
1709                 }
1711                 return $ret;
1712         }
1714         function GetCol($sql, $inputarr = false, $trim = false) {
1716                 $rs = $this->Execute($sql, $inputarr);
1717                 if ($rs) {
1718                         $rv = array();
1719                         if ($trim) {
1720                                 while (!$rs->EOF) {
1721                                         $rv[] = trim(reset($rs->fields));
1722                                         $rs->MoveNext();
1723                                 }
1724                         } else {
1725                                 while (!$rs->EOF) {
1726                                         $rv[] = reset($rs->fields);
1727                                         $rs->MoveNext();
1728                                 }
1729                         }
1730                         $rs->Close();
1731                 } else {
1732                         $rv = false;
1733                 }
1734                 return $rv;
1735         }
1737         function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) {
1738                 $rs = $this->CacheExecute($secs, $sql, $inputarr);
1739                 if ($rs) {
1740                         $rv = array();
1741                         if ($trim) {
1742                                 while (!$rs->EOF) {
1743                                         $rv[] = trim(reset($rs->fields));
1744                                         $rs->MoveNext();
1745                                 }
1746                         } else {
1747                                 while (!$rs->EOF) {
1748                                         $rv[] = reset($rs->fields);
1749                                         $rs->MoveNext();
1750                                 }
1751                         }
1752                         $rs->Close();
1753                 } else
1754                         $rv = false;
1756                 return $rv;
1757         }
1759         function Transpose(&$rs,$addfieldnames=true) {
1760                 $rs2 = $this->_rs2rs($rs);
1761                 if (!$rs2) {
1762                         return false;
1763                 }
1765                 $rs2->_transpose($addfieldnames);
1766                 return $rs2;
1767         }
1769         /*
1770                 Calculate the offset of a date for a particular database and generate
1771                         appropriate SQL. Useful for calculating future/past dates and storing
1772                         in a database.
1774                 If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1775         */
1776         function OffsetDate($dayFraction,$date=false) {
1777                 if (!$date) {
1778                         $date = $this->sysDate;
1779                 }
1780                 return  '('.$date.'+'.$dayFraction.')';
1781         }
1784         /**
1785         *
1786         * @param sql                    SQL statement
1787         * @param [inputarr]             input bind array
1788         */
1789         function GetArray($sql,$inputarr=false) {
1790                 global $ADODB_COUNTRECS;
1792                 $savec = $ADODB_COUNTRECS;
1793                 $ADODB_COUNTRECS = false;
1794                 $rs = $this->Execute($sql,$inputarr);
1795                 $ADODB_COUNTRECS = $savec;
1796                 if (!$rs)
1797                         if (defined('ADODB_PEAR')) {
1798                                 $cls = ADODB_PEAR_Error();
1799                                 return $cls;
1800                         } else {
1801                                 return false;
1802                         }
1803                 $arr = $rs->GetArray();
1804                 $rs->Close();
1805                 return $arr;
1806         }
1808         function CacheGetAll($secs2cache,$sql=false,$inputarr=false) {
1809                 $arr = $this->CacheGetArray($secs2cache,$sql,$inputarr);
1810                 return $arr;
1811         }
1813         function CacheGetArray($secs2cache,$sql=false,$inputarr=false) {
1814                 global $ADODB_COUNTRECS;
1816                 $savec = $ADODB_COUNTRECS;
1817                 $ADODB_COUNTRECS = false;
1818                 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1819                 $ADODB_COUNTRECS = $savec;
1821                 if (!$rs)
1822                         if (defined('ADODB_PEAR')) {
1823                                 $cls = ADODB_PEAR_Error();
1824                                 return $cls;
1825                         } else {
1826                                 return false;
1827                         }
1828                 $arr = $rs->GetArray();
1829                 $rs->Close();
1830                 return $arr;
1831         }
1833         function GetRandRow($sql, $arr= false) {
1834                 $rezarr = $this->GetAll($sql, $arr);
1835                 $sz = sizeof($rezarr);
1836                 return $rezarr[abs(rand()) % $sz];
1837         }
1839         /**
1840         * Return one row of sql statement. Recordset is disposed for you.
1841         * Note that SelectLimit should not be called.
1842         *
1843         * @param sql                    SQL statement
1844         * @param [inputarr]             input bind array
1845         */
1846         function GetRow($sql,$inputarr=false) {
1847                 global $ADODB_COUNTRECS;
1849                 $crecs = $ADODB_COUNTRECS;
1850                 $ADODB_COUNTRECS = false;
1852                 $rs = $this->Execute($sql,$inputarr);
1854                 $ADODB_COUNTRECS = $crecs;
1855                 if ($rs) {
1856                         if (!$rs->EOF) {
1857                                 $arr = $rs->fields;
1858                         } else {
1859                                 $arr = array();
1860                         }
1861                         $rs->Close();
1862                         return $arr;
1863                 }
1865                 return false;
1866         }
1868         function CacheGetRow($secs2cache,$sql=false,$inputarr=false) {
1869                 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1870                 if ($rs) {
1871                         if (!$rs->EOF) {
1872                                 $arr = $rs->fields;
1873                         } else {
1874                                 $arr = array();
1875                         }
1877                         $rs->Close();
1878                         return $arr;
1879                 }
1880                 return false;
1881         }
1883         /**
1884         * Insert or replace a single record. Note: this is not the same as MySQL's replace.
1885         * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1886         * Also note that no table locking is done currently, so it is possible that the
1887         * record be inserted twice by two programs...
1888         *
1889         * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1890         *
1891         * $table                table name
1892         * $fieldArray   associative array of data (you must quote strings yourself).
1893         * $keyCol               the primary key field name or if compound key, array of field names
1894         * autoQuote             set to true to use a hueristic to quote strings. Works with nulls and numbers
1895         *                                       but does not work with dates nor SQL functions.
1896         * has_autoinc   the primary key is an auto-inc field, so skip in insert.
1897         *
1898         * Currently blob replace not supported
1899         *
1900         * returns 0 = fail, 1 = update, 2 = insert
1901         */
1903         function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) {
1904                 global $ADODB_INCLUDED_LIB;
1905                 if (empty($ADODB_INCLUDED_LIB)) {
1906                         include(ADODB_DIR.'/adodb-lib.inc.php');
1907                 }
1909                 return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1910         }
1913         /**
1914          * Will select, getting rows from $offset (1-based), for $nrows.
1915          * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1916          * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1917          * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1918          * eg.
1919          *  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1920          *  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1921          *
1922          * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1923          *
1924          * @param [secs2cache]  seconds to cache data, set to 0 to force query. This is optional
1925          * @param sql
1926          * @param [offset]      is the row to start calculations from (1-based)
1927          * @param [nrows]       is the number of rows to get
1928          * @param [inputarr]    array of bind variables
1929          * @return              the recordset ($rs->databaseType == 'array')
1930          */
1931         function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) {
1932                 if (!is_numeric($secs2cache)) {
1933                         if ($sql === false) {
1934                                 $sql = -1;
1935                         }
1936                         if ($offset == -1) {
1937                                 $offset = false;
1938                         }
1939                                                                                                 // sql, nrows, offset,inputarr
1940                         $rs = $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1941                 } else {
1942                         if ($sql === false) {
1943                                 $this->outp_throw("Warning: \$sql missing from CacheSelectLimit()",'CacheSelectLimit');
1944                         }
1945                         $rs = $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1946                 }
1947                 return $rs;
1948         }
1950         /**
1951          * Flush cached recordsets that match a particular $sql statement.
1952          * If $sql == false, then we purge all files in the cache.
1953          */
1954         function CacheFlush($sql=false,$inputarr=false) {
1955                 global $ADODB_CACHE_DIR, $ADODB_CACHE;
1957                 # Create cache if it does not exist
1958                 if (empty($ADODB_CACHE)) {
1959                         $this->_CreateCache();
1960                 }
1962                 if (!$sql) {
1963                         $ADODB_CACHE->flushall($this->debug);
1964                         return;
1965                 }
1967                 $f = $this->_gencachename($sql.serialize($inputarr),false);
1968                 return $ADODB_CACHE->flushcache($f, $this->debug);
1969         }
1972         /**
1973          * Private function to generate filename for caching.
1974          * Filename is generated based on:
1975          *
1976          *  - sql statement
1977          *  - database type (oci8, ibase, ifx, etc)
1978          *  - database name
1979          *  - userid
1980          *  - setFetchMode (adodb 4.23)
1981          *
1982          * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
1983          * Assuming that we can have 50,000 files per directory with good performance,
1984          * then we can scale to 12.8 million unique cached recordsets. Wow!
1985          */
1986         function _gencachename($sql,$createdir) {
1987                 global $ADODB_CACHE, $ADODB_CACHE_DIR;
1989                 if ($this->fetchMode === false) {
1990                         global $ADODB_FETCH_MODE;
1991                         $mode = $ADODB_FETCH_MODE;
1992                 } else {
1993                         $mode = $this->fetchMode;
1994                 }
1995                 $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1996                 if (!$ADODB_CACHE->createdir) {
1997                         return $m;
1998                 }
1999                 if (!$createdir) {
2000                         $dir = $ADODB_CACHE->getdirname($m);
2001                 } else {
2002                         $dir = $ADODB_CACHE->createdir($m, $this->debug);
2003                 }
2005                 return $dir.'/adodb_'.$m.'.cache';
2006         }
2009         /**
2010          * Execute SQL, caching recordsets.
2011          *
2012          * @param [secs2cache]  seconds to cache data, set to 0 to force query.
2013          *                                        This is an optional parameter.
2014          * @param sql           SQL statement to execute
2015          * @param [inputarr]    holds the input data  to bind to
2016          * @return              RecordSet or false
2017          */
2018         function CacheExecute($secs2cache,$sql=false,$inputarr=false) {
2019                 global $ADODB_CACHE;
2021                 if (empty($ADODB_CACHE)) {
2022                         $this->_CreateCache();
2023                 }
2025                 if (!is_numeric($secs2cache)) {
2026                         $inputarr = $sql;
2027                         $sql = $secs2cache;
2028                         $secs2cache = $this->cacheSecs;
2029                 }
2031                 if (is_array($sql)) {
2032                         $sqlparam = $sql;
2033                         $sql = $sql[0];
2034                 } else
2035                         $sqlparam = $sql;
2038                 $md5file = $this->_gencachename($sql.serialize($inputarr),true);
2039                 $err = '';
2041                 if ($secs2cache > 0){
2042                         $rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
2043                         $this->numCacheHits += 1;
2044                 } else {
2045                         $err='Timeout 1';
2046                         $rs = false;
2047                         $this->numCacheMisses += 1;
2048                 }
2050                 if (!$rs) {
2051                 // no cached rs found
2052                         if ($this->debug) {
2053                                 if (get_magic_quotes_runtime() && !$this->memCache) {
2054                                         ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
2055                                 }
2056                                 if ($this->debug !== -1) {
2057                                         ADOConnection::outp( " $md5file cache failure: $err (this is a notice and not an error)");
2058                                 }
2059                         }
2061                         $rs = $this->Execute($sqlparam,$inputarr);
2063                         if ($rs) {
2064                                 $eof = $rs->EOF;
2065                                 $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
2066                                 $rs->timeCreated = time(); // used by caching
2067                                 $txt = _rs2serialize($rs,false,$sql); // serialize
2069                                 $ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
2070                                 if (!$ok) {
2071                                         if ($ok === false) {
2072                                                 $em = 'Cache write error';
2073                                                 $en = -32000;
2075                                                 if ($fn = $this->raiseErrorFn) {
2076                                                         $fn($this->databaseType,'CacheExecute', $en, $em, $md5file,$sql,$this);
2077                                                 }
2078                                         } else {
2079                                                 $em = 'Cache file locked warning';
2080                                                 $en = -32001;
2081                                                 // do not call error handling for just a warning
2082                                         }
2084                                         if ($this->debug) {
2085                                                 ADOConnection::outp( " ".$em);
2086                                         }
2087                                 }
2088                                 if ($rs->EOF && !$eof) {
2089                                         $rs->MoveFirst();
2090                                         //$rs = csv2rs($md5file,$err);
2091                                         $rs->connection = $this; // Pablo suggestion
2092                                 }
2094                         } else if (!$this->memCache) {
2095                                 $ADODB_CACHE->flushcache($md5file);
2096                         }
2097                 } else {
2098                         $this->_errorMsg = '';
2099                         $this->_errorCode = 0;
2101                         if ($this->fnCacheExecute) {
2102                                 $fn = $this->fnCacheExecute;
2103                                 $fn($this, $secs2cache, $sql, $inputarr);
2104                         }
2105                         // ok, set cached object found
2106                         $rs->connection = $this; // Pablo suggestion
2107                         if ($this->debug){
2108                                 if ($this->debug == 99) {
2109                                         adodb_backtrace();
2110                                 }
2111                                 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
2112                                 $ttl = $rs->timeCreated + $secs2cache - time();
2113                                 $s = is_array($sql) ? $sql[0] : $sql;
2114                                 if ($inBrowser) {
2115                                         $s = '<i>'.htmlspecialchars($s).'</i>';
2116                                 }
2118                                 ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
2119                         }
2120                 }
2121                 return $rs;
2122         }
2125         /*
2126                 Similar to PEAR DB's autoExecute(), except that
2127                 $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
2128                 If $mode == 'UPDATE', then $where is compulsory as a safety measure.
2130                 $forceUpdate means that even if the data has not changed, perform update.
2131          */
2132         function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = false, $forceUpdate = true, $magicq = false) {
2133                 if ($where === false && ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) ) {
2134                         $this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause', 'AutoExecute');
2135                         return false;
2136                 }
2138                 $sql = "SELECT * FROM $table";
2139                 $rs = $this->SelectLimit($sql, 1);
2140                 if (!$rs) {
2141                         return false; // table does not exist
2142                 }
2144                 $rs->tableName = $table;
2145                 if ($where !== false) {
2146                         $sql .= " WHERE $where";
2147                 }
2148                 $rs->sql = $sql;
2150                 switch($mode) {
2151                         case 'UPDATE':
2152                         case DB_AUTOQUERY_UPDATE:
2153                                 $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
2154                                 break;
2155                         case 'INSERT':
2156                         case DB_AUTOQUERY_INSERT:
2157                                 $sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
2158                                 break;
2159                         default:
2160                                 $this->outp_throw("AutoExecute: Unknown mode=$mode", 'AutoExecute');
2161                                 return false;
2162                 }
2163                 return $sql && $this->Execute($sql);
2164         }
2167         /**
2168          * Generates an Update Query based on an existing recordset.
2169          * $arrFields is an associative array of fields with the value
2170          * that should be assigned.
2171          *
2172          * Note: This function should only be used on a recordset
2173          *         that is run against a single table and sql should only
2174          *               be a simple select stmt with no groupby/orderby/limit
2175          *
2176          * "Jonathan Younger" <jyounger@unilab.com>
2177          */
2178         function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null) {
2179                 global $ADODB_INCLUDED_LIB;
2181                 // ********************************************************
2182                 // This is here to maintain compatibility
2183                 // with older adodb versions. Sets force type to force nulls if $forcenulls is set.
2184                 if (!isset($force)) {
2185                         global $ADODB_FORCE_TYPE;
2186                         $force = $ADODB_FORCE_TYPE;
2187                 }
2188                 // ********************************************************
2190                 if (empty($ADODB_INCLUDED_LIB)) {
2191                         include(ADODB_DIR.'/adodb-lib.inc.php');
2192                 }
2193                 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
2194         }
2196         /**
2197          * Generates an Insert Query based on an existing recordset.
2198          * $arrFields is an associative array of fields with the value
2199          * that should be assigned.
2200          *
2201          * Note: This function should only be used on a recordset
2202          *       that is run against a single table.
2203          */
2204         function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null) {
2205                 global $ADODB_INCLUDED_LIB;
2206                 if (!isset($force)) {
2207                         global $ADODB_FORCE_TYPE;
2208                         $force = $ADODB_FORCE_TYPE;
2209                 }
2210                 if (empty($ADODB_INCLUDED_LIB)) {
2211                         include(ADODB_DIR.'/adodb-lib.inc.php');
2212                 }
2213                 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
2214         }
2217         /**
2218         * Update a blob column, given a where clause. There are more sophisticated
2219         * blob handling functions that we could have implemented, but all require
2220         * a very complex API. Instead we have chosen something that is extremely
2221         * simple to understand and use.
2222         *
2223         * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
2224         *
2225         * Usage to update a $blobvalue which has a primary key blob_id=1 into a
2226         * field blobtable.blobcolumn:
2227         *
2228         *       UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
2229         *
2230         * Insert example:
2231         *
2232         *       $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2233         *       $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
2234         */
2235         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') {
2236                 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
2237         }
2239         /**
2240         * Usage:
2241         *       UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
2242         *
2243         *       $blobtype supports 'BLOB' and 'CLOB'
2244         *
2245         *       $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2246         *       $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
2247         */
2248         function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') {
2249                 $fd = fopen($path,'rb');
2250                 if ($fd === false) {
2251                         return false;
2252                 }
2253                 $val = fread($fd,filesize($path));
2254                 fclose($fd);
2255                 return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
2256         }
2258         function BlobDecode($blob) {
2259                 return $blob;
2260         }
2262         function BlobEncode($blob) {
2263                 return $blob;
2264         }
2266         function GetCharSet() {
2267                 return $this->charSet;
2268         }
2270         function SetCharSet($charset) {
2271                 $this->charSet = $charset;
2272                 return true;
2273         }
2275         function IfNull( $field, $ifNull ) {
2276                 return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
2277         }
2279         function LogSQL($enable=true) {
2280                 include_once(ADODB_DIR.'/adodb-perf.inc.php');
2282                 if ($enable) {
2283                         $this->fnExecute = 'adodb_log_sql';
2284                 } else {
2285                         $this->fnExecute = false;
2286                 }
2288                 $old = $this->_logsql;
2289                 $this->_logsql = $enable;
2290                 if ($enable && !$old) {
2291                         $this->_affected = false;
2292                 }
2293                 return $old;
2294         }
2296         /**
2297         * Usage:
2298         *       UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
2299         *
2300         *       $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
2301         *       $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
2302         */
2303         function UpdateClob($table,$column,$val,$where) {
2304                 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
2305         }
2307         // not the fastest implementation - quick and dirty - jlim
2308         // for best performance, use the actual $rs->MetaType().
2309         function MetaType($t,$len=-1,$fieldobj=false) {
2311                 if (empty($this->_metars)) {
2312                         $rsclass = $this->rsPrefix.$this->databaseType;
2313                         $this->_metars = new $rsclass(false,$this->fetchMode);
2314                         $this->_metars->connection = $this;
2315                 }
2316                 return $this->_metars->MetaType($t,$len,$fieldobj);
2317         }
2320         /**
2321         *  Change the SQL connection locale to a specified locale.
2322         *  This is used to get the date formats written depending on the client locale.
2323         */
2324         function SetDateLocale($locale = 'En') {
2325                 $this->locale = $locale;
2326                 switch (strtoupper($locale))
2327                 {
2328                         case 'EN':
2329                                 $this->fmtDate="'Y-m-d'";
2330                                 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2331                                 break;
2333                         case 'US':
2334                                 $this->fmtDate = "'m-d-Y'";
2335                                 $this->fmtTimeStamp = "'m-d-Y H:i:s'";
2336                                 break;
2338                         case 'PT_BR':
2339                         case 'NL':
2340                         case 'FR':
2341                         case 'RO':
2342                         case 'IT':
2343                                 $this->fmtDate="'d-m-Y'";
2344                                 $this->fmtTimeStamp = "'d-m-Y H:i:s'";
2345                                 break;
2347                         case 'GE':
2348                                 $this->fmtDate="'d.m.Y'";
2349                                 $this->fmtTimeStamp = "'d.m.Y H:i:s'";
2350                                 break;
2352                         default:
2353                                 $this->fmtDate="'Y-m-d'";
2354                                 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2355                                 break;
2356                 }
2357         }
2359         /**
2360          * GetActiveRecordsClass Performs an 'ALL' query
2361          *
2362          * @param mixed $class This string represents the class of the current active record
2363          * @param mixed $table Table used by the active record object
2364          * @param mixed $whereOrderBy Where, order, by clauses
2365          * @param mixed $bindarr
2366          * @param mixed $primkeyArr
2367          * @param array $extra Query extras: limit, offset...
2368          * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
2369          * @access public
2370          * @return void
2371          */
2372         function GetActiveRecordsClass(
2373                         $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
2374                         $extra=array(),
2375                         $relations=array())
2376         {
2377                 global $_ADODB_ACTIVE_DBS;
2378                 ## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
2379                 ## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
2380                 if (!isset($_ADODB_ACTIVE_DBS)) {
2381                         include_once(ADODB_DIR.'/adodb-active-record.inc.php');
2382                 }
2383                 return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
2384         }
2386         function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false) {
2387                 $arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2388                 return $arr;
2389         }
2391         /**
2392          * Close Connection
2393          */
2394         function Close() {
2395                 $rez = $this->_close();
2396                 $this->_connectionID = false;
2397                 return $rez;
2398         }
2400         /**
2401          * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2402          *
2403          * @return true if succeeded or false if database does not support transactions
2404          */
2405         function BeginTrans() {
2406                 if ($this->debug) {
2407                         ADOConnection::outp("BeginTrans: Transactions not supported for this driver");
2408                 }
2409                 return false;
2410         }
2412         /* set transaction mode */
2413         function SetTransactionMode( $transaction_mode ) {
2414                 $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2415                 $this->_transmode  = $transaction_mode;
2416         }
2417 /*
2418 http://msdn2.microsoft.com/en-US/ms173763.aspx
2419 http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2420 http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2421 http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2422 */
2423         function MetaTransaction($mode,$db) {
2424                 $mode = strtoupper($mode);
2425                 $mode = str_replace('ISOLATION LEVEL ','',$mode);
2427                 switch($mode) {
2429                 case 'READ UNCOMMITTED':
2430                         switch($db) {
2431                         case 'oci8':
2432                         case 'oracle':
2433                                 return 'ISOLATION LEVEL READ COMMITTED';
2434                         default:
2435                                 return 'ISOLATION LEVEL READ UNCOMMITTED';
2436                         }
2437                         break;
2439                 case 'READ COMMITTED':
2440                                 return 'ISOLATION LEVEL READ COMMITTED';
2441                         break;
2443                 case 'REPEATABLE READ':
2444                         switch($db) {
2445                         case 'oci8':
2446                         case 'oracle':
2447                                 return 'ISOLATION LEVEL SERIALIZABLE';
2448                         default:
2449                                 return 'ISOLATION LEVEL REPEATABLE READ';
2450                         }
2451                         break;
2453                 case 'SERIALIZABLE':
2454                                 return 'ISOLATION LEVEL SERIALIZABLE';
2455                         break;
2457                 default:
2458                         return $mode;
2459                 }
2460         }
2462         /**
2463          * If database does not support transactions, always return true as data always commited
2464          *
2465          * @param $ok  set to false to rollback transaction, true to commit
2466          *
2467          * @return true/false.
2468          */
2469         function CommitTrans($ok=true) {
2470                 return true;
2471         }
2474         /**
2475          * If database does not support transactions, rollbacks always fail, so return false
2476          *
2477          * @return true/false.
2478          */
2479         function RollbackTrans() {
2480                 return false;
2481         }
2484         /**
2485          * return the databases that the driver can connect to.
2486          * Some databases will return an empty array.
2487          *
2488          * @return an array of database names.
2489          */
2490         function MetaDatabases() {
2491                 global $ADODB_FETCH_MODE;
2493                 if ($this->metaDatabasesSQL) {
2494                         $save = $ADODB_FETCH_MODE;
2495                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2497                         if ($this->fetchMode !== false) {
2498                                 $savem = $this->SetFetchMode(false);
2499                         }
2501                         $arr = $this->GetCol($this->metaDatabasesSQL);
2502                         if (isset($savem)) {
2503                                 $this->SetFetchMode($savem);
2504                         }
2505                         $ADODB_FETCH_MODE = $save;
2507                         return $arr;
2508                 }
2510                 return false;
2511         }
2513         /**
2514          * List procedures or functions in an array.
2515          * @param procedureNamePattern  a procedure name pattern; must match the procedure name as it is stored in the database
2516          * @param catalog a catalog name; must match the catalog name as it is stored in the database;
2517          * @param schemaPattern a schema name pattern;
2518          *
2519          * @return array of procedures on current database.
2520          *
2521          * Array(
2522          *   [name_of_procedure] => Array(
2523          *     [type] => PROCEDURE or FUNCTION
2524          *     [catalog] => Catalog_name
2525          *     [schema] => Schema_name
2526          *     [remarks] => explanatory comment on the procedure
2527          *   )
2528          * )
2529          */
2530         function MetaProcedures($procedureNamePattern = null, $catalog  = null, $schemaPattern  = null) {
2531                 return false;
2532         }
2535         /**
2536          * @param ttype can either be 'VIEW' or 'TABLE' or false.
2537          *              If false, both views and tables are returned.
2538          *              "VIEW" returns only views
2539          *              "TABLE" returns only tables
2540          * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2541          * @param mask  is the input mask - only supported by oci8 and postgresql
2542          *
2543          * @return  array of tables for current database.
2544          */
2545         function MetaTables($ttype=false,$showSchema=false,$mask=false) {
2546                 global $ADODB_FETCH_MODE;
2548                 if ($mask) {
2549                         return false;
2550                 }
2551                 if ($this->metaTablesSQL) {
2552                         $save = $ADODB_FETCH_MODE;
2553                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2555                         if ($this->fetchMode !== false) {
2556                                 $savem = $this->SetFetchMode(false);
2557                         }
2559                         $rs = $this->Execute($this->metaTablesSQL);
2560                         if (isset($savem)) {
2561                                 $this->SetFetchMode($savem);
2562                         }
2563                         $ADODB_FETCH_MODE = $save;
2565                         if ($rs === false) {
2566                                 return false;
2567                         }
2568                         $arr = $rs->GetArray();
2569                         $arr2 = array();
2571                         if ($hast = ($ttype && isset($arr[0][1]))) {
2572                                 $showt = strncmp($ttype,'T',1);
2573                         }
2575                         for ($i=0; $i < sizeof($arr); $i++) {
2576                                 if ($hast) {
2577                                         if ($showt == 0) {
2578                                                 if (strncmp($arr[$i][1],'T',1) == 0) {
2579                                                         $arr2[] = trim($arr[$i][0]);
2580                                                 }
2581                                         } else {
2582                                                 if (strncmp($arr[$i][1],'V',1) == 0) {
2583                                                         $arr2[] = trim($arr[$i][0]);
2584                                                 }
2585                                         }
2586                                 } else
2587                                         $arr2[] = trim($arr[$i][0]);
2588                         }
2589                         $rs->Close();
2590                         return $arr2;
2591                 }
2592                 return false;
2593         }
2596         function _findschema(&$table,&$schema) {
2597                 if (!$schema && ($at = strpos($table,'.')) !== false) {
2598                         $schema = substr($table,0,$at);
2599                         $table = substr($table,$at+1);
2600                 }
2601         }
2603         /**
2604          * List columns in a database as an array of ADOFieldObjects.
2605          * See top of file for definition of object.
2606          *
2607          * @param $table        table name to query
2608          * @param $normalize    makes table name case-insensitive (required by some databases)
2609          * @schema is optional database schema to use - not supported by all databases.
2610          *
2611          * @return  array of ADOFieldObjects for current table.
2612          */
2613         function MetaColumns($table,$normalize=true) {
2614                 global $ADODB_FETCH_MODE;
2616                 if (!empty($this->metaColumnsSQL)) {
2617                         $schema = false;
2618                         $this->_findschema($table,$schema);
2620                         $save = $ADODB_FETCH_MODE;
2621                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2622                         if ($this->fetchMode !== false) {
2623                                 $savem = $this->SetFetchMode(false);
2624                         }
2625                         $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2626                         if (isset($savem)) {
2627                                 $this->SetFetchMode($savem);
2628                         }
2629                         $ADODB_FETCH_MODE = $save;
2630                         if ($rs === false || $rs->EOF) {
2631                                 return false;
2632                         }
2634                         $retarr = array();
2635                         while (!$rs->EOF) { //print_r($rs->fields);
2636                                 $fld = new ADOFieldObject();
2637                                 $fld->name = $rs->fields[0];
2638                                 $fld->type = $rs->fields[1];
2639                                 if (isset($rs->fields[3]) && $rs->fields[3]) {
2640                                         if ($rs->fields[3]>0) {
2641                                                 $fld->max_length = $rs->fields[3];
2642                                         }
2643                                         $fld->scale = $rs->fields[4];
2644                                         if ($fld->scale>0) {
2645                                                 $fld->max_length += 1;
2646                                         }
2647                                 } else {
2648                                         $fld->max_length = $rs->fields[2];
2649                                 }
2651                                 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) {
2652                                         $retarr[] = $fld;
2653                                 } else {
2654                                         $retarr[strtoupper($fld->name)] = $fld;
2655                                 }
2656                                 $rs->MoveNext();
2657                         }
2658                         $rs->Close();
2659                         return $retarr;
2660                 }
2661                 return false;
2662         }
2664         /**
2665          * List indexes on a table as an array.
2666          * @param table  table name to query
2667          * @param primary true to only show primary keys. Not actually used for most databases
2668          *
2669          * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2670          *
2671          * Array(
2672          *   [name_of_index] => Array(
2673          *     [unique] => true or false
2674          *     [columns] => Array(
2675          *       [0] => firstname
2676          *       [1] => lastname
2677          *     )
2678          *   )
2679          * )
2680          */
2681         function MetaIndexes($table, $primary = false, $owner = false) {
2682                 return false;
2683         }
2685         /**
2686          * List columns names in a table as an array.
2687          * @param table table name to query
2688          *
2689          * @return  array of column names for current table.
2690          */
2691         function MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) {
2692                 $objarr = $this->MetaColumns($table);
2693                 if (!is_array($objarr)) {
2694                         return false;
2695                 }
2696                 $arr = array();
2697                 if ($numIndexes) {
2698                         $i = 0;
2699                         if ($useattnum) {
2700                                 foreach($objarr as $v)
2701                                         $arr[$v->attnum] = $v->name;
2703                         } else
2704                                 foreach($objarr as $v) $arr[$i++] = $v->name;
2705                 } else
2706                         foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2708                 return $arr;
2709         }
2711         /**
2712          * Different SQL databases used different methods to combine strings together.
2713          * This function provides a wrapper.
2714          *
2715          * param s      variable number of string parameters
2716          *
2717          * Usage: $db->Concat($str1,$str2);
2718          *
2719          * @return concatenated string
2720          */
2721         function Concat() {
2722                 $arr = func_get_args();
2723                 return implode($this->concat_operator, $arr);
2724         }
2727         /**
2728          * Converts a date "d" to a string that the database can understand.
2729          *
2730          * @param d     a date in Unix date time format.
2731          *
2732          * @return  date string in database date format
2733          */
2734         function DBDate($d, $isfld=false) {
2735                 if (empty($d) && $d !== 0) {
2736                         return 'null';
2737                 }
2738                 if ($isfld) {
2739                         return $d;
2740                 }
2741                 if (is_object($d)) {
2742                         return $d->format($this->fmtDate);
2743                 }
2745                 if (is_string($d) && !is_numeric($d)) {
2746                         if ($d === 'null') {
2747                                 return $d;
2748                         }
2749                         if (strncmp($d,"'",1) === 0) {
2750                                 $d = _adodb_safedateq($d);
2751                                 return $d;
2752                         }
2753                         if ($this->isoDates) {
2754                                 return "'$d'";
2755                         }
2756                         $d = ADOConnection::UnixDate($d);
2757                 }
2759                 return adodb_date($this->fmtDate,$d);
2760         }
2762         function BindDate($d) {
2763                 $d = $this->DBDate($d);
2764                 if (strncmp($d,"'",1)) {
2765                         return $d;
2766                 }
2768                 return substr($d,1,strlen($d)-2);
2769         }
2771         function BindTimeStamp($d) {
2772                 $d = $this->DBTimeStamp($d);
2773                 if (strncmp($d,"'",1)) {
2774                         return $d;
2775                 }
2777                 return substr($d,1,strlen($d)-2);
2778         }
2781         /**
2782          * Converts a timestamp "ts" to a string that the database can understand.
2783          *
2784          * @param ts    a timestamp in Unix date time format.
2785          *
2786          * @return  timestamp string in database timestamp format
2787          */
2788         function DBTimeStamp($ts,$isfld=false) {
2789                 if (empty($ts) && $ts !== 0) {
2790                         return 'null';
2791                 }
2792                 if ($isfld) {
2793                         return $ts;
2794                 }
2795                 if (is_object($ts)) {
2796                         return $ts->format($this->fmtTimeStamp);
2797                 }
2799                 # strlen(14) allows YYYYMMDDHHMMSS format
2800                 if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) {
2801                         return adodb_date($this->fmtTimeStamp,$ts);
2802                 }
2804                 if ($ts === 'null') {
2805                         return $ts;
2806                 }
2807                 if ($this->isoDates && strlen($ts) !== 14) {
2808                         $ts = _adodb_safedate($ts);
2809                         return "'$ts'";
2810                 }
2811                 $ts = ADOConnection::UnixTimeStamp($ts);
2812                 return adodb_date($this->fmtTimeStamp,$ts);
2813         }
2815         /**
2816          * Also in ADORecordSet.
2817          * @param $v is a date string in YYYY-MM-DD format
2818          *
2819          * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2820          */
2821         static function UnixDate($v) {
2822                 if (is_object($v)) {
2823                 // odbtp support
2824                 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2825                         return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2826                 }
2828                 if (is_numeric($v) && strlen($v) !== 8) {
2829                         return $v;
2830                 }
2831                 if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", $v, $rr)) {
2832                         return false;
2833                 }
2835                 if ($rr[1] <= TIMESTAMP_FIRST_YEAR) {
2836                         return 0;
2837                 }
2839                 // h-m-s-MM-DD-YY
2840                 return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2841         }
2844         /**
2845          * Also in ADORecordSet.
2846          * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2847          *
2848          * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2849          */
2850         static function UnixTimeStamp($v) {
2851                 if (is_object($v)) {
2852                 // odbtp support
2853                 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2854                         return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2855                 }
2857                 if (!preg_match(
2858                         "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
2859                         ($v), $rr)) return false;
2861                 if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) {
2862                         return 0;
2863                 }
2865                 // h-m-s-MM-DD-YY
2866                 if (!isset($rr[5])) {
2867                         return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2868                 }
2869                 return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2870         }
2872         /**
2873          * Also in ADORecordSet.
2874          *
2875          * Format database date based on user defined format.
2876          *
2877          * @param v             is the character date in YYYY-MM-DD format, returned by database
2878          * @param fmt   is the format to apply to it, using date()
2879          *
2880          * @return a date formated as user desires
2881          */
2882         function UserDate($v,$fmt='Y-m-d',$gmt=false) {
2883                 $tt = $this->UnixDate($v);
2885                 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2886                 if (($tt === false || $tt == -1) && $v != false) {
2887                         return $v;
2888                 } else if ($tt == 0) {
2889                         return $this->emptyDate;
2890                 } else if ($tt == -1) {
2891                         // pre-TIMESTAMP_FIRST_YEAR
2892                 }
2894                 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2896         }
2898         /**
2899          *
2900          * @param v             is the character timestamp in YYYY-MM-DD hh:mm:ss format
2901          * @param fmt   is the format to apply to it, using date()
2902          *
2903          * @return a timestamp formated as user desires
2904          */
2905         function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) {
2906                 if (!isset($v)) {
2907                         return $this->emptyTimeStamp;
2908                 }
2909                 # strlen(14) allows YYYYMMDDHHMMSS format
2910                 if (is_numeric($v) && strlen($v)<14) {
2911                         return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2912                 }
2913                 $tt = $this->UnixTimeStamp($v);
2914                 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2915                 if (($tt === false || $tt == -1) && $v != false) {
2916                         return $v;
2917                 }
2918                 if ($tt == 0) {
2919                         return $this->emptyTimeStamp;
2920                 }
2921                 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2922         }
2924         function escape($s,$magic_quotes=false) {
2925                 return $this->addq($s,$magic_quotes);
2926         }
2928         /**
2929         * Quotes a string, without prefixing nor appending quotes.
2930         */
2931         function addq($s,$magic_quotes=false) {
2932                 if (!$magic_quotes) {
2933                         if ($this->replaceQuote[0] == '\\') {
2934                                 // only since php 4.0.5
2935                                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2936                                 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2937                         }
2938                         return  str_replace("'",$this->replaceQuote,$s);
2939                 }
2941                 // undo magic quotes for "
2942                 $s = str_replace('\\"','"',$s);
2944                 if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) {
2945                         // ' already quoted, no need to change anything
2946                         return $s;
2947                 } else {
2948                         // change \' to '' for sybase/mssql
2949                         $s = str_replace('\\\\','\\',$s);
2950                         return str_replace("\\'",$this->replaceQuote,$s);
2951                 }
2952         }
2954         /**
2955          * Correctly quotes a string so that all strings are escaped. We prefix and append
2956          * to the string single-quotes.
2957          * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
2958          *
2959          * @param s                     the string to quote
2960          * @param [magic_quotes]        if $s is GET/POST var, set to get_magic_quotes_gpc().
2961          *                              This undoes the stupidity of magic quotes for GPC.
2962          *
2963          * @return  quoted string to be sent back to database
2964          */
2965         function qstr($s,$magic_quotes=false) {
2966                 if (!$magic_quotes) {
2967                         if ($this->replaceQuote[0] == '\\'){
2968                                 // only since php 4.0.5
2969                                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2970                                 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2971                         }
2972                         return  "'".str_replace("'",$this->replaceQuote,$s)."'";
2973                 }
2975                 // undo magic quotes for "
2976                 $s = str_replace('\\"','"',$s);
2978                 if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) {
2979                         // ' already quoted, no need to change anything
2980                         return "'$s'";
2981                 } else {
2982                         // change \' to '' for sybase/mssql
2983                         $s = str_replace('\\\\','\\',$s);
2984                         return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2985                 }
2986         }
2989         /**
2990         * Will select the supplied $page number from a recordset, given that it is paginated in pages of
2991         * $nrows rows per page. It also saves two boolean values saying if the given page is the first
2992         * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2993         *
2994         * See docs-adodb.htm#ex8 for an example of usage.
2995         *
2996         * @param sql
2997         * @param nrows          is the number of rows per page to get
2998         * @param page           is the page number to get (1-based)
2999         * @param [inputarr]     array of bind variables
3000         * @param [secs2cache]           is a private parameter only used by jlim
3001         * @return               the recordset ($rs->databaseType == 'array')
3002         *
3003         * NOTE: phpLens uses a different algorithm and does not use PageExecute().
3004         *
3005         */
3006         function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) {
3007                 global $ADODB_INCLUDED_LIB;
3008                 if (empty($ADODB_INCLUDED_LIB)) {
3009                         include(ADODB_DIR.'/adodb-lib.inc.php');
3010                 }
3011                 if ($this->pageExecuteCountRows) {
3012                         $rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3013                 } else {
3014                         $rs = _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3015                 }
3016                 return $rs;
3017         }
3020         /**
3021         * Will select the supplied $page number from a recordset, given that it is paginated in pages of
3022         * $nrows rows per page. It also saves two boolean values saying if the given page is the first
3023         * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
3024         *
3025         * @param secs2cache     seconds to cache data, set to 0 to force query
3026         * @param sql
3027         * @param nrows          is the number of rows per page to get
3028         * @param page           is the page number to get (1-based)
3029         * @param [inputarr]     array of bind variables
3030         * @return               the recordset ($rs->databaseType == 'array')
3031         */
3032         function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) {
3033                 /*switch($this->dataProvider) {
3034                 case 'postgres':
3035                 case 'mysql':
3036                         break;
3037                 default: $secs2cache = 0; break;
3038                 }*/
3039                 $rs = $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
3040                 return $rs;
3041         }
3043 } // end class ADOConnection
3047         //==============================================================================================
3048         // CLASS ADOFetchObj
3049         //==============================================================================================
3051         /**
3052         * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
3053         */
3054         class ADOFetchObj {
3055         };
3057         //==============================================================================================
3058         // CLASS ADORecordSet_empty
3059         //==============================================================================================
3061         class ADODB_Iterator_empty implements Iterator {
3063                 private $rs;
3065                 function __construct($rs) {
3066                         $this->rs = $rs;
3067                 }
3069                 function rewind() {}
3071                 function valid() {
3072                         return !$this->rs->EOF;
3073                 }
3075                 function key() {
3076                         return false;
3077                 }
3079                 function current() {
3080                         return false;
3081                 }
3083                 function next() {}
3085                 function __call($func, $params) {
3086                         return call_user_func_array(array($this->rs, $func), $params);
3087                 }
3089                 function hasMore() {
3090                         return false;
3091                 }
3093         }
3096         /**
3097         * Lightweight recordset when there are no records to be returned
3098         */
3099         class ADORecordSet_empty implements IteratorAggregate
3100         {
3101                 var $dataProvider = 'empty';
3102                 var $databaseType = false;
3103                 var $EOF = true;
3104                 var $_numOfRows = 0;
3105                 var $fields = false;
3106                 var $connection = false;
3108                 function RowCount() {
3109                         return 0;
3110                 }
3112                 function RecordCount() {
3113                         return 0;
3114                 }
3116                 function PO_RecordCount() {
3117                         return 0;
3118                 }
3120                 function Close() {
3121                         return true;
3122                 }
3124                 function FetchRow() {
3125                         return false;
3126                 }
3128                 function FieldCount() {
3129                         return 0;
3130                 }
3132                 function Init() {}
3134                 function getIterator() {
3135                         return new ADODB_Iterator_empty($this);
3136                 }
3138                 function GetAssoc() {
3139                         return array();
3140                 }
3142                 function GetArray() {
3143                         return array();
3144                 }
3146                 function GetAll() {
3147                         return array();
3148                 }
3150                 function GetArrayLimit() {
3151                         return array();
3152                 }
3154                 function GetRows() {
3155                         return array();
3156                 }
3158                 function GetRowAssoc() {
3159                         return array();
3160                 }
3162                 function MaxRecordCount() {
3163                         return 0;
3164                 }
3166                 function NumRows() {
3167                         return 0;
3168                 }
3170                 function NumCols() {
3171                         return 0;
3172                 }
3173         }
3175         //==============================================================================================
3176         // DATE AND TIME FUNCTIONS
3177         //==============================================================================================
3178         if (!defined('ADODB_DATE_VERSION')) {
3179                 include(ADODB_DIR.'/adodb-time.inc.php');
3180         }
3182         //==============================================================================================
3183         // CLASS ADORecordSet
3184         //==============================================================================================
3186         class ADODB_Iterator implements Iterator {
3188                 private $rs;
3190                 function __construct($rs) {
3191                         $this->rs = $rs;
3192                 }
3194                 function rewind() {
3195                         $this->rs->MoveFirst();
3196                 }
3198                 function valid() {
3199                         return !$this->rs->EOF;
3200                 }
3202                 function key() {
3203                         return $this->rs->_currentRow;
3204                 }
3206                 function current() {
3207                         return $this->rs->fields;
3208                 }
3210                 function next() {
3211                         $this->rs->MoveNext();
3212                 }
3214                 function __call($func, $params) {
3215                         return call_user_func_array(array($this->rs, $func), $params);
3216                 }
3218                 function hasMore() {
3219                         return !$this->rs->EOF;
3220                 }
3222         }
3225         /**
3226          * RecordSet class that represents the dataset returned by the database.
3227          * To keep memory overhead low, this class holds only the current row in memory.
3228          * No prefetching of data is done, so the RecordCount() can return -1 ( which
3229          * means recordcount not known).
3230          */
3231         class ADORecordSet implements IteratorAggregate {
3233         /**
3234          * public variables
3235          */
3236         var $dataProvider = "native";
3237         var $fields = false;    /// holds the current row data
3238         var $blobSize = 100;    /// any varchar/char field this size or greater is treated as a blob
3239                                                         /// in other words, we use a text area for editing.
3240         var $canSeek = false;   /// indicates that seek is supported
3241         var $sql;                               /// sql text
3242         var $EOF = false;               /// Indicates that the current record position is after the last record in a Recordset object.
3244         var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
3245         var $emptyDate = '&nbsp;'; /// what to display when $time==0
3246         var $debug = false;
3247         var $timeCreated=0;             /// datetime in Unix format rs created -- for cached recordsets
3249         var $bind = false;              /// used by Fields() to hold array - should be private?
3250         var $fetchMode;                 /// default fetch mode
3251         var $connection = false; /// the parent connection
3253         /**
3254          *      private variables
3255          */
3256         var $_numOfRows = -1;   /** number of rows, or -1 */
3257         var $_numOfFields = -1; /** number of fields in recordset */
3258         var $_queryID = -1;             /** This variable keeps the result link identifier.     */
3259         var $_currentRow = -1;  /** This variable keeps the current row in the Recordset.       */
3260         var $_closed = false;   /** has recordset been closed */
3261         var $_inited = false;   /** Init() should only be called once */
3262         var $_obj;                              /** Used by FetchObj */
3263         var $_names;                    /** Used by FetchObj */
3265         var $_currentPage = -1; /** Added by Iván Oliva to implement recordset pagination */
3266         var $_atFirstPage = false;      /** Added by Iván Oliva to implement recordset pagination */
3267         var $_atLastPage = false;       /** Added by Iván Oliva to implement recordset pagination */
3268         var $_lastPageNo = -1;
3269         var $_maxRecordCount = 0;
3270         var $datetime = false;
3272         /**
3273          * Constructor
3274          *
3275          * @param queryID       this is the queryID returned by ADOConnection->_query()
3276          *
3277          */
3278         function __construct($queryID) {
3279                 $this->_queryID = $queryID;
3280         }
3282         function __destruct() {
3283                 @$this->Close();
3284         }
3286         function getIterator() {
3287                 return new ADODB_Iterator($this);
3288         }
3290         /* this is experimental - i don't really know what to return... */
3291         function __toString() {
3292                 include_once(ADODB_DIR.'/toexport.inc.php');
3293                 return _adodb_export($this,',',',',false,true);
3294         }
3296         function Init() {
3297                 if ($this->_inited) {
3298                         return;
3299                 }
3300                 $this->_inited = true;
3301                 if ($this->_queryID) {
3302                         @$this->_initrs();
3303                 } else {
3304                         $this->_numOfRows = 0;
3305                         $this->_numOfFields = 0;
3306                 }
3307                 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
3308                         $this->_currentRow = 0;
3309                         if ($this->EOF = ($this->_fetch() === false)) {
3310                                 $this->_numOfRows = 0; // _numOfRows could be -1
3311                         }
3312                 } else {
3313                         $this->EOF = true;
3314                 }
3315         }
3318         /**
3319          * Generate a SELECT tag string from a recordset, and return the string.
3320          * If the recordset has 2 cols, we treat the 1st col as the containing
3321          * the text to display to the user, and 2nd col as the return value. Default
3322          * strings are compared with the FIRST column.
3323          *
3324          * @param name                  name of SELECT tag
3325          * @param [defstr]              the value to hilite. Use an array for multiple hilites for listbox.
3326          * @param [blank1stItem]        true to leave the 1st item in list empty
3327          * @param [multiple]            true for listbox, false for popup
3328          * @param [size]                #rows to show for listbox. not used by popup
3329          * @param [selectAttr]          additional attributes to defined for SELECT tag.
3330          *                              useful for holding javascript onChange='...' handlers.
3331          & @param [compareFields0]      when we have 2 cols in recordset, we compare the defstr with
3332          *                              column 0 (1st col) if this is true. This is not documented.
3333          *
3334          * @return HTML
3335          *
3336          * changes by glen.davies@cce.ac.nz to support multiple hilited items
3337          */
3338         function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
3339                         $size=0, $selectAttr='',$compareFields0=true)
3340         {
3341                 global $ADODB_INCLUDED_LIB;
3342                 if (empty($ADODB_INCLUDED_LIB)) {
3343                         include(ADODB_DIR.'/adodb-lib.inc.php');
3344                 }
3345                 return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
3346                         $size, $selectAttr,$compareFields0);
3347         }
3351         /**
3352          * Generate a SELECT tag string from a recordset, and return the string.
3353          * If the recordset has 2 cols, we treat the 1st col as the containing
3354          * the text to display to the user, and 2nd col as the return value. Default
3355          * strings are compared with the SECOND column.
3356          *
3357          */
3358         function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='') {
3359                 return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
3360                         $size, $selectAttr,false);
3361         }
3363         /*
3364                 Grouped Menu
3365         */
3366         function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
3367                         $size=0, $selectAttr='')
3368         {
3369                 global $ADODB_INCLUDED_LIB;
3370                 if (empty($ADODB_INCLUDED_LIB)) {
3371                         include(ADODB_DIR.'/adodb-lib.inc.php');
3372                 }
3373                 return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
3374                         $size, $selectAttr,false);
3375         }
3377         /**
3378          * return recordset as a 2-dimensional array.
3379          *
3380          * @param [nRows]  is the number of rows to return. -1 means every row.
3381          *
3382          * @return an array indexed by the rows (0-based) from the recordset
3383          */
3384         function GetArray($nRows = -1) {
3385                 global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
3386                 $results = adodb_getall($this,$nRows);
3387                 return $results;
3388         }
3389                 $results = array();
3390                 $cnt = 0;
3391                 while (!$this->EOF && $nRows != $cnt) {
3392                         $results[] = $this->fields;
3393                         $this->MoveNext();
3394                         $cnt++;
3395                 }
3396                 return $results;
3397         }
3399         function GetAll($nRows = -1) {
3400                 $arr = $this->GetArray($nRows);
3401                 return $arr;
3402         }
3404         /*
3405         * Some databases allow multiple recordsets to be returned. This function
3406         * will return true if there is a next recordset, or false if no more.
3407         */
3408         function NextRecordSet() {
3409                 return false;
3410         }
3412         /**
3413          * return recordset as a 2-dimensional array.
3414          * Helper function for ADOConnection->SelectLimit()
3415          *
3416          * @param offset        is the row to start calculations from (1-based)
3417          * @param [nrows]       is the number of rows to return
3418          *
3419          * @return an array indexed by the rows (0-based) from the recordset
3420          */
3421         function GetArrayLimit($nrows,$offset=-1) {
3422                 if ($offset <= 0) {
3423                         $arr = $this->GetArray($nrows);
3424                         return $arr;
3425                 }
3427                 $this->Move($offset);
3429                 $results = array();
3430                 $cnt = 0;
3431                 while (!$this->EOF && $nrows != $cnt) {
3432                         $results[$cnt++] = $this->fields;
3433                         $this->MoveNext();
3434                 }
3436                 return $results;
3437         }
3440         /**
3441          * Synonym for GetArray() for compatibility with ADO.
3442          *
3443          * @param [nRows]  is the number of rows to return. -1 means every row.
3444          *
3445          * @return an array indexed by the rows (0-based) from the recordset
3446          */
3447         function GetRows($nRows = -1) {
3448                 $arr = $this->GetArray($nRows);
3449                 return $arr;
3450         }
3452         /**
3453          * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
3454          * The first column is treated as the key and is not included in the array.
3455          * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
3456          * $force_array == true.
3457          *
3458          * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
3459          * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
3460          * read the source.
3461          *
3462          * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
3463          * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
3464          *
3465          * @return an associative array indexed by the first column of the array,
3466          * or false if the  data has less than 2 cols.
3467          */
3468         function GetAssoc($force_array = false, $first2cols = false) {
3469                 global $ADODB_EXTENSION;
3471                 $cols = $this->_numOfFields;
3472                 if ($cols < 2) {
3473                         return false;
3474                 }
3476                 // Empty recordset
3477                 if (!$this->fields) {
3478                         return array();
3479                 }
3481                 // Determine whether the array is associative or 0-based numeric
3482                 $numIndex = array_keys($this->fields) == range(0, count($this->fields) - 1);
3484                 $results = array();
3486                 if (!$first2cols && ($cols > 2 || $force_array)) {
3487                         if ($ADODB_EXTENSION) {
3488                                 if ($numIndex) {
3489                                         while (!$this->EOF) {
3490                                                 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
3491                                                 adodb_movenext($this);
3492                                         }
3493                                 } else {
3494                                         while (!$this->EOF) {
3495                                         // Fix for array_slice re-numbering numeric associative keys