source: sandbox/2.5.1-evolucao/phpgwapi/inc/adodb/adodb.inc.php @ 8222

Revision 8222, 123.6 KB checked in by angelo, 11 years ago (diff)

Ticket #3491 - Compatibilizar Expresso com novas versoes do PHP

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
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/*
2194http://msdn2.microsoft.com/en-US/ms173763.aspx
2195http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2196http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2197http://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?>
Note: See TracBrowser for help on using the repository browser.