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