source: trunk/phpgwapi/inc/adodb/adodb.inc.php @ 2

Revision 2, 101.9 KB checked in by niltonneto, 17 years ago (diff)

Removida todas as tags usadas pelo CVS ($Id, $Source).
Primeira versão no CVS externo.

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