source: branches/comu-ver2/data/module/adodb/adodb.inc.php @ 18701

Revision 18701, 118.4 KB checked in by nanasess, 14 years ago (diff)

Copyright の更新(#601)

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