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