MDL-58546 libraries: Patch ADOdb for PHP 7.2 compat
[moodle.git] / lib / adodb / adodb.inc.php
1 <?php
2 /*
3  * Set tabs to 4 for best viewing.
4  *
5  * Latest version is available at http://adodb.sourceforge.net
6  *
7  * This is the main include file for ADOdb.
8  * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
9  *
10  * The ADOdb files are formatted so that doxygen can be used to generate documentation.
11  * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
12  */
14 /**
15         \mainpage
17         @version   v5.20.9  21-Dec-2016
18         @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
19         @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
21         Released under both BSD license and Lesser GPL library license. You can choose which license
22         you prefer.
24         PHP's database access functions are not standardised. This creates a need for a database
25         class library to hide the differences between the different database API's (encapsulate
26         the differences) so we can easily switch databases.
28         We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
29         Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
30         ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
31         other databases via ODBC.
33         Latest Download at http://adodb.sourceforge.net/
35  */
37 if (!defined('_ADODB_LAYER')) {
38         define('_ADODB_LAYER',1);
40         // The ADOdb extension is no longer maintained and effectively unsupported
41         // since v5.04. The library will not function properly if it is present.
42         if(defined('ADODB_EXTENSION')) {
43                 $msg = "Unsupported ADOdb Extension (v" . ADODB_EXTENSION . ") detected! "
44                         . "Disable it to use ADOdb";
46                 $errorfn = defined('ADODB_ERROR_HANDLER') ? ADODB_ERROR_HANDLER : false;
47                 if ($errorfn) {
48                         $conn = false;
49                         $errorfn('ADOdb', basename(__FILE__), -9999, $msg, null, null, $conn);
50                 } else {
51                         die($msg . PHP_EOL);
52                 }
53         }
55         //==============================================================================================
56         // CONSTANT DEFINITIONS
57         //==============================================================================================
60         /**
61          * Set ADODB_DIR to the directory where this file resides...
62          * This constant was formerly called $ADODB_RootPath
63          */
64         if (!defined('ADODB_DIR')) {
65                 define('ADODB_DIR',dirname(__FILE__));
66         }
68         //==============================================================================================
69         // GLOBAL VARIABLES
70         //==============================================================================================
72         GLOBAL
73                 $ADODB_vers,            // database version
74                 $ADODB_COUNTRECS,       // count number of records returned - slows down query
75                 $ADODB_CACHE_DIR,       // directory to cache recordsets
76                 $ADODB_CACHE,
77                 $ADODB_CACHE_CLASS,
78                 $ADODB_EXTENSION,   // ADODB extension installed
79                 $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
80                 $ADODB_FETCH_MODE,      // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
81                 $ADODB_GETONE_EOF,
82                 $ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.
84         //==============================================================================================
85         // GLOBAL SETUP
86         //==============================================================================================
88         $ADODB_EXTENSION = defined('ADODB_EXTENSION');
90         // ********************************************************
91         // Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
92         // Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
93         //
94         // 0 = ignore empty fields. All empty fields in array are ignored.
95         // 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
96         // 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
97         // 3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values.
99                 define('ADODB_FORCE_IGNORE',0);
100                 define('ADODB_FORCE_NULL',1);
101                 define('ADODB_FORCE_EMPTY',2);
102                 define('ADODB_FORCE_VALUE',3);
103         // ********************************************************
106         if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
108                 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
110         // allow [ ] @ ` " and . in table names
111                 define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
113         // prefetching used by oracle
114                 if (!defined('ADODB_PREFETCH_ROWS')) {
115                         define('ADODB_PREFETCH_ROWS',10);
116                 }
119         /**
120          * Fetch mode
121          *
122          * Set global variable $ADODB_FETCH_MODE to one of these constants or use
123          * the SetFetchMode() method to control how recordset fields are returned
124          * when fetching data.
125          *
126          *   - NUM:     array()
127          *   - ASSOC:   array('id' => 456, 'name' => 'john')
128          *   - BOTH:    array(0 => 456, 'id' => 456, 1 => 'john', 'name' => 'john')
129          *   - DEFAULT: driver-dependent
130          */
131                 define('ADODB_FETCH_DEFAULT', 0);
132                 define('ADODB_FETCH_NUM', 1);
133                 define('ADODB_FETCH_ASSOC', 2);
134                 define('ADODB_FETCH_BOTH', 3);
136         /**
137          * Associative array case constants
138          *
139          * By defining the ADODB_ASSOC_CASE constant to one of these values, it is
140          * possible to control the case of field names (associative array's keys)
141          * when operating in ADODB_FETCH_ASSOC fetch mode.
142          *   - LOWER:  $rs->fields['orderid']
143          *   - UPPER:  $rs->fields['ORDERID']
144          *   - NATIVE: $rs->fields['OrderID'] (or whatever the RDBMS will return)
145          *
146          * The default is to use native case-names.
147          *
148          * NOTE: This functionality is not implemented everywhere, it currently
149          * works only with: mssql, odbc, oci8 and ibase derived drivers
150          */
151                 define('ADODB_ASSOC_CASE_LOWER', 0);
152                 define('ADODB_ASSOC_CASE_UPPER', 1);
153                 define('ADODB_ASSOC_CASE_NATIVE', 2);
156                 if (!defined('TIMESTAMP_FIRST_YEAR')) {
157                         define('TIMESTAMP_FIRST_YEAR',100);
158                 }
160                 /**
161                  * AutoExecute constants
162                  * (moved from adodb-pear.inc.php since they are only used in here)
163                  */
164                 define('DB_AUTOQUERY_INSERT', 1);
165                 define('DB_AUTOQUERY_UPDATE', 2);
168                 // PHP's version scheme makes converting to numbers difficult - workaround
169                 $_adodb_ver = (float) PHP_VERSION;
170                 if ($_adodb_ver >= 5.2) {
171                         define('ADODB_PHPVER',0x5200);
172                 } else if ($_adodb_ver >= 5.0) {
173                         define('ADODB_PHPVER',0x5000);
174                 } else {
175                         die("PHP5 or later required. You are running ".PHP_VERSION);
176                 }
177                 unset($_adodb_ver);
178         }
181         /**
182                 Accepts $src and $dest arrays, replacing string $data
183         */
184         function ADODB_str_replace($src, $dest, $data) {
185                 if (ADODB_PHPVER >= 0x4050) {
186                         return str_replace($src,$dest,$data);
187                 }
189                 $s = reset($src);
190                 $d = reset($dest);
191                 while ($s !== false) {
192                         $data = str_replace($s,$d,$data);
193                         $s = next($src);
194                         $d = next($dest);
195                 }
196                 return $data;
197         }
199         function ADODB_Setup() {
200         GLOBAL
201                 $ADODB_vers,            // database version
202                 $ADODB_COUNTRECS,       // count number of records returned - slows down query
203                 $ADODB_CACHE_DIR,       // directory to cache recordsets
204                 $ADODB_FETCH_MODE,
205                 $ADODB_CACHE,
206                 $ADODB_CACHE_CLASS,
207                 $ADODB_FORCE_TYPE,
208                 $ADODB_GETONE_EOF,
209                 $ADODB_QUOTE_FIELDNAMES;
211                 if (empty($ADODB_CACHE_CLASS)) {
212                         $ADODB_CACHE_CLASS =  'ADODB_Cache_File' ;
213                 }
214                 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
215                 $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
216                 $ADODB_GETONE_EOF = null;
218                 if (!isset($ADODB_CACHE_DIR)) {
219                         $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
220                 } else {
221                         // do not accept url based paths, eg. http:/ or ftp:/
222                         if (strpos($ADODB_CACHE_DIR,'://') !== false) {
223                                 die("Illegal path http:// or ftp://");
224                         }
225                 }
228                 // Initialize random number generator for randomizing cache flushes
229                 // -- note Since PHP 4.2.0, the seed  becomes optional and defaults to a random value if omitted.
230                 // MDL-41198 Removed random seed initialization.
231                 // srand(((double)microtime())*1000000);
233                 /**
234                  * ADODB version as a string.
235                  */
236                 $ADODB_vers = 'v5.20.9  21-Dec-2016';
238                 /**
239                  * Determines whether recordset->RecordCount() is used.
240                  * Set to false for highest performance -- RecordCount() will always return -1 then
241                  * for databases that provide "virtual" recordcounts...
242                  */
243                 if (!isset($ADODB_COUNTRECS)) {
244                         $ADODB_COUNTRECS = true;
245                 }
246         }
249         //==============================================================================================
250         // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
251         //==============================================================================================
253         ADODB_Setup();
255         //==============================================================================================
256         // CLASS ADOFieldObject
257         //==============================================================================================
258         /**
259          * Helper class for FetchFields -- holds info on a column
260          */
261         class ADOFieldObject {
262                 var $name = '';
263                 var $max_length=0;
264                 var $type="";
265 /*
266                 // additional fields by dannym... (danny_milo@yahoo.com)
267                 var $not_null = false;
268                 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
269                 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
271                 var $has_default = false; // this one I have done only in mysql and postgres for now ...
272                         // others to come (dannym)
273                 var $default_value; // default, if any, and supported. Check has_default first.
274 */
275         }
278         function _adodb_safedate($s) {
279                 return str_replace(array("'", '\\'), '', $s);
280         }
282         // parse date string to prevent injection attack
283         // date string will have one quote at beginning e.g. '3434343'
284         function _adodb_safedateq($s) {
285                 $len = strlen($s);
286                 if ($s[0] !== "'") {
287                         $s2 = "'".$s[0];
288                 } else {
289                         $s2 = "'";
290                 }
291                 for($i=1; $i<$len; $i++) {
292                         $ch = $s[$i];
293                         if ($ch === '\\') {
294                                 $s2 .= "'";
295                                 break;
296                         } elseif ($ch === "'") {
297                                 $s2 .= $ch;
298                                 break;
299                         }
301                         $s2 .= $ch;
302                 }
304                 return strlen($s2) == 0 ? 'null' : $s2;
305         }
308         // for transaction handling
310         function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) {
311                 //print "Errorno ($fn errno=$errno m=$errmsg) ";
312                 $thisConnection->_transOK = false;
313                 if ($thisConnection->_oldRaiseFn) {
314                         $fn = $thisConnection->_oldRaiseFn;
315                         $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
316                 }
317         }
319         //------------------
320         // class for caching
321         class ADODB_Cache_File {
323                 var $createdir = true; // requires creation of temp dirs
325                 function __construct() {
326                         global $ADODB_INCLUDED_CSV;
327                         if (empty($ADODB_INCLUDED_CSV)) {
328                                 include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
329                         }
330                 }
332                 // write serialised recordset to cache item/file
333                 function writecache($filename, $contents,  $debug, $secs2cache) {
334                         return adodb_write_file($filename, $contents,$debug);
335                 }
337                 // load serialised recordset and unserialise it
338                 function &readcache($filename, &$err, $secs2cache, $rsClass) {
339                         $rs = csv2rs($filename,$err,$secs2cache,$rsClass);
340                         return $rs;
341                 }
343                 // flush all items in cache
344                 function flushall($debug=false) {
345                         global $ADODB_CACHE_DIR;
347                         $rez = false;
349                         if (strlen($ADODB_CACHE_DIR) > 1) {
350                                 $rez = $this->_dirFlush($ADODB_CACHE_DIR);
351                                 if ($debug) {
352                                         ADOConnection::outp( "flushall: $ADODB_CACHE_DIR<br><pre>\n". $rez."</pre>");
353                                 }
354                         }
355                         return $rez;
356                 }
358                 // flush one file in cache
359                 function flushcache($f, $debug=false) {
360                         if (!@unlink($f)) {
361                                 if ($debug) {
362                                         ADOConnection::outp( "flushcache: failed for $f");
363                                 }
364                         }
365                 }
367                 function getdirname($hash) {
368                         global $ADODB_CACHE_DIR;
369                         if (!isset($this->notSafeMode)) {
370                                 $this->notSafeMode = !ini_get('safe_mode');
371                         }
372                         return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
373                 }
375                 // create temp directories
376                 function createdir($hash, $debug) {
377                         global $ADODB_CACHE_PERMS;
379                         $dir = $this->getdirname($hash);
380                         if ($this->notSafeMode && !file_exists($dir)) {
381                                 $oldu = umask(0);
382                                 if (!@mkdir($dir, empty($ADODB_CACHE_PERMS) ? 0771 : $ADODB_CACHE_PERMS)) {
383                                         if(!is_dir($dir) && $debug) {
384                                                 ADOConnection::outp("Cannot create $dir");
385                                         }
386                                 }
387                                 umask($oldu);
388                         }
390                         return $dir;
391                 }
393                 /**
394                 * Private function to erase all of the files and subdirectories in a directory.
395                 *
396                 * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
397                 * Note: $kill_top_level is used internally in the function to flush subdirectories.
398                 */
399                 function _dirFlush($dir, $kill_top_level = false) {
400                         if(!$dh = @opendir($dir)) return;
402                         while (($obj = readdir($dh))) {
403                                 if($obj=='.' || $obj=='..') continue;
404                                 $f = $dir.'/'.$obj;
406                                 if (strpos($obj,'.cache')) {
407                                         @unlink($f);
408                                 }
409                                 if (is_dir($f)) {
410                                         $this->_dirFlush($f, true);
411                                 }
412                         }
413                         if ($kill_top_level === true) {
414                                 @rmdir($dir);
415                         }
416                         return true;
417                 }
418         }
420         //==============================================================================================
421         // CLASS ADOConnection
422         //==============================================================================================
424         /**
425          * Connection object. For connecting to databases, and executing queries.
426          */
427         abstract class ADOConnection {
428         //
429         // PUBLIC VARS
430         //
431         var $dataProvider = 'native';
432         var $databaseType = '';         /// RDBMS currently in use, eg. odbc, mysql, mssql
433         var $database = '';                     /// Name of database to be used.
434         var $host = '';                         /// The hostname of the database server
435         var $user = '';                         /// The username which is used to connect to the database server.
436         var $password = '';                     /// Password for the username. For security, we no longer store it.
437         var $debug = false;                     /// if set to true will output sql statements
438         var $maxblobsize = 262144;      /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
439         var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
440         var $substr = 'substr';         /// substring operator
441         var $length = 'length';         /// string length ofperator
442         var $random = 'rand()';         /// random function
443         var $upperCase = 'upper';               /// uppercase function
444         var $fmtDate = "'Y-m-d'";       /// used by DBDate() as the default date format used by the database
445         var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
446         var $true = '1';                        /// string that represents TRUE for a database
447         var $false = '0';                       /// string that represents FALSE for a database
448         var $replaceQuote = "\\'";      /// string to use to replace quotes
449         var $nameQuote = '"';           /// string to use to quote identifiers and names
450         var $charSet=false;                     /// character set to use - only for interbase, postgres and oci8
451         var $metaDatabasesSQL = '';
452         var $metaTablesSQL = '';
453         var $uniqueOrderBy = false; /// All order by columns have to be unique
454         var $emptyDate = '&nbsp;';
455         var $emptyTimeStamp = '&nbsp;';
456         var $lastInsID = false;
457         //--
458         var $hasInsertID = false;               /// supports autoincrement ID?
459         var $hasAffectedRows = false;   /// supports affected rows for update/delete?
460         var $hasTop = false;                    /// support mssql/access SELECT TOP 10 * FROM TABLE
461         var $hasLimit = false;                  /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
462         var $readOnly = false;                  /// this is a readonly database - used by phpLens
463         var $hasMoveFirst = false;              /// has ability to run MoveFirst(), scrolling backwards
464         var $hasGenID = false;                  /// can generate sequences using GenID();
465         var $hasTransactions = true;    /// has transactions
466         //--
467         var $genID = 0;                                 /// sequence id used by GenID();
468         var $raiseErrorFn = false;              /// error function to call
469         var $isoDates = false;                  /// accepts dates in ISO format
470         var $cacheSecs = 3600;                  /// cache for 1 hour
472         // memcache
473         var $memCache = false; /// should we use memCache instead of caching in files
474         var $memCacheHost; /// memCache host
475         var $memCachePort = 11211; /// memCache port
476         var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
478         var $sysDate = false; /// name of function that returns the current date
479         var $sysTimeStamp = false; /// name of function that returns the current timestamp
480         var $sysUTimeStamp = false; // name of function that returns the current timestamp accurate to the microsecond or nearest fraction
481         var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
483         var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
484         var $numCacheHits = 0;
485         var $numCacheMisses = 0;
486         var $pageExecuteCountRows = true;
487         var $uniqueSort = false; /// indicates that all fields in order by must be unique
488         var $leftOuter = false; /// operator to use for left outer join in WHERE clause
489         var $rightOuter = false; /// operator to use for right outer join in WHERE clause
490         var $ansiOuter = false; /// whether ansi outer join syntax supported
491         var $autoRollback = false; // autoRollback on PConnect().
492         var $poorAffectedRows = false; // affectedRows not working or unreliable
494         var $fnExecute = false;
495         var $fnCacheExecute = false;
496         var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
497         var $rsPrefix = "ADORecordSet_";
499         var $autoCommit = true;         /// do not modify this yourself - actually private
500         var $transOff = 0;                      /// temporarily disable transactions
501         var $transCnt = 0;                      /// count of nested transactions
503         var $fetchMode=false;
505         var $null2null = 'null'; // in autoexecute/getinsertsql/getupdatesql, this value will be converted to a null
506         var $bulkBind = false; // enable 2D Execute array
507         //
508         // PRIVATE VARS
509         //
510         var $_oldRaiseFn =  false;
511         var $_transOK = null;
512         var $_connectionID      = false;        /// The returned link identifier whenever a successful database connection is made.
513         var $_errorMsg = false;         /// A variable which was used to keep the returned last error message.  The value will
514                                                                 /// then returned by the errorMsg() function
515         var $_errorCode = false;        /// Last error code, not guaranteed to be used - only by oci8
516         var $_queryID = false;          /// This variable keeps the last created result link identifier
518         var $_isPersistentConnection = false;   /// A boolean variable to state whether its a persistent connection or normal connection.       */
519         var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
520         var $_evalAll = false;
521         var $_affected = false;
522         var $_logsql = false;
523         var $_transmode = ''; // transaction mode
525         /*
526          * Additional parameters that may be passed to drivers in the connect string
527          * Driver must be coded to accept the parameters
528          */
529         protected $connectionParameters = array();
531         /**
532         * Adds a parameter to the connection string.
533         *
534         * These parameters are added to the connection string when connecting,
535         * if the driver is coded to use it.
536         *
537         * @param        string  $parameter      The name of the parameter to set
538         * @param        string  $value          The value of the parameter
539         *
540         * @return null
541         *
542         * @example, for mssqlnative driver ('CharacterSet','UTF-8')
543         */
544         final public function setConnectionParameter($parameter,$value)
545         {
547                 $this->connectionParameters[$parameter] = $value;
549         }
551         static function Version() {
552                 global $ADODB_vers;
554                 // Semantic Version number matching regex
555                 $regex = '^[vV]?(\d+\.\d+\.\d+'         // Version number (X.Y.Z) with optional 'V'
556                         . '(?:-(?:'                         // Optional preprod version: a '-'
557                         . 'dev|'                            // followed by 'dev'
558                         . '(?:(?:alpha|beta|rc)(?:\.\d+))'  // or a preprod suffix and version number
559                         . '))?)(?:\s|$)';                   // Whitespace or end of string
561                 if (!preg_match("/$regex/", $ADODB_vers, $matches)) {
562                         // This should normally not happen... Return whatever is between the start
563                         // of the string and the first whitespace (or the end of the string).
564                         self::outp("Invalid version number: '$ADODB_vers'", 'Version');
565                         $regex = '^[vV]?(.*?)(?:\s|$)';
566                         preg_match("/$regex/", $ADODB_vers, $matches);
567                 }
568                 return $matches[1];
569         }
571         /**
572                 Get server version info...
574                 @returns An array with 2 elements: $arr['string'] is the description string,
575                         and $arr[version] is the version (also a string).
576         */
577         function ServerInfo() {
578                 return array('description' => '', 'version' => '');
579         }
581         function IsConnected() {
582                 return !empty($this->_connectionID);
583         }
585         function _findvers($str) {
586                 if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) {
587                         return $arr[1];
588                 } else {
589                         return '';
590                 }
591         }
593         /**
594         * All error messages go through this bottleneck function.
595         * You can define your own handler by defining the function name in ADODB_OUTP.
596         */
597         static function outp($msg,$newline=true) {
598                 global $ADODB_FLUSH,$ADODB_OUTP;
600                 if (defined('ADODB_OUTP')) {
601                         $fn = ADODB_OUTP;
602                         $fn($msg,$newline);
603                         return;
604                 } else if (isset($ADODB_OUTP)) {
605                         $fn = $ADODB_OUTP;
606                         $fn($msg,$newline);
607                         return;
608                 }
610                 if ($newline) {
611                         $msg .= "<br>\n";
612                 }
614                 if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) {
615                         echo $msg;
616                 } else {
617                         echo strip_tags($msg);
618                 }
621                 if (!empty($ADODB_FLUSH) && ob_get_length() !== false) {
622                         flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan
623                 }
625         }
627         function Time() {
628                 $rs = $this->_Execute("select $this->sysTimeStamp");
629                 if ($rs && !$rs->EOF) {
630                         return $this->UnixTimeStamp(reset($rs->fields));
631                 }
633                 return false;
634         }
636         /**
637          * Connect to database
638          *
639          * @param [argHostname]         Host to connect to
640          * @param [argUsername]         Userid to login
641          * @param [argPassword]         Associated password
642          * @param [argDatabaseName]     database
643          * @param [forceNew]            force new connection
644          *
645          * @return true or false
646          */
647         function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) {
648                 if ($argHostname != "") {
649                         $this->host = $argHostname;
650                 }
651                 if ( strpos($this->host, ':') > 0 && isset($this->port) ) {
652                         list($this->host, $this->port) = explode(":", $this->host, 2);
653                 }
654                 if ($argUsername != "") {
655                         $this->user = $argUsername;
656                 }
657                 if ($argPassword != "") {
658                         $this->password = 'not stored'; // not stored for security reasons
659                 }
660                 if ($argDatabaseName != "") {
661                         $this->database = $argDatabaseName;
662                 }
664                 $this->_isPersistentConnection = false;
666                 if ($forceNew) {
667                         if ($rez=$this->_nconnect($this->host, $this->user, $argPassword, $this->database)) {
668                                 return true;
669                         }
670                 } else {
671                         if ($rez=$this->_connect($this->host, $this->user, $argPassword, $this->database)) {
672                                 return true;
673                         }
674                 }
675                 if (isset($rez)) {
676                         $err = $this->ErrorMsg();
677                         $errno = $this->ErrorNo();
678                         if (empty($err)) {
679                                 $err = "Connection error to server '$argHostname' with user '$argUsername'";
680                         }
681                 } else {
682                         $err = "Missing extension for ".$this->dataProvider;
683                         $errno = 0;
684                 }
685                 if ($fn = $this->raiseErrorFn) {
686                         $fn($this->databaseType, 'CONNECT', $errno, $err, $this->host, $this->database, $this);
687                 }
689                 $this->_connectionID = false;
690                 if ($this->debug) {
691                         ADOConnection::outp( $this->host.': '.$err);
692                 }
693                 return false;
694         }
696         function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) {
697                 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
698         }
701         /**
702          * Always force a new connection to database - currently only works with oracle
703          *
704          * @param [argHostname]         Host to connect to
705          * @param [argUsername]         Userid to login
706          * @param [argPassword]         Associated password
707          * @param [argDatabaseName]     database
708          *
709          * @return true or false
710          */
711         function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
712                 return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
713         }
715         /**
716          * Establish persistent connect to database
717          *
718          * @param [argHostname]         Host to connect to
719          * @param [argUsername]         Userid to login
720          * @param [argPassword]         Associated password
721          * @param [argDatabaseName]     database
722          *
723          * @return return true or false
724          */
725         function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
727                 if (defined('ADODB_NEVER_PERSIST')) {
728                         return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
729                 }
731                 if ($argHostname != "") {
732                         $this->host = $argHostname;
733                 }
734                 if ( strpos($this->host, ':') > 0 && isset($this->port) ) {
735                         list($this->host, $this->port) = explode(":", $this->host, 2);
736                 }
737                 if ($argUsername != "") {
738                         $this->user = $argUsername;
739                 }
740                 if ($argPassword != "") {
741                         $this->password = 'not stored';
742                 }
743                 if ($argDatabaseName != "") {
744                         $this->database = $argDatabaseName;
745                 }
747                 $this->_isPersistentConnection = true;
749                 if ($rez = $this->_pconnect($this->host, $this->user, $argPassword, $this->database)) {
750                         return true;
751                 }
752                 if (isset($rez)) {
753                         $err = $this->ErrorMsg();
754                         if (empty($err)) {
755                                 $err = "Connection error to server '$argHostname' with user '$argUsername'";
756                         }
757                         $ret = false;
758                 } else {
759                         $err = "Missing extension for ".$this->dataProvider;
760                         $ret = 0;
761                 }
762                 if ($fn = $this->raiseErrorFn) {
763                         $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
764                 }
766                 $this->_connectionID = false;
767                 if ($this->debug) {
768                         ADOConnection::outp( $this->host.': '.$err);
769                 }
770                 return $ret;
771         }
773         function outp_throw($msg,$src='WARN',$sql='') {
774                 if (defined('ADODB_ERROR_HANDLER') &&  ADODB_ERROR_HANDLER == 'adodb_throw') {
775                         adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
776                         return;
777                 }
778                 ADOConnection::outp($msg);
779         }
781         // create cache class. Code is backward compat with old memcache implementation
782         function _CreateCache() {
783                 global $ADODB_CACHE, $ADODB_CACHE_CLASS;
785                 if ($this->memCache) {
786                         global $ADODB_INCLUDED_MEMCACHE;
788                         if (empty($ADODB_INCLUDED_MEMCACHE)) {
789                                 include_once(ADODB_DIR.'/adodb-memcache.lib.inc.php');
790                         }
791                         $ADODB_CACHE = new ADODB_Cache_MemCache($this);
792                 } else {
793                         $ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
794                 }
795         }
797         // Format date column in sql string given an input format that understands Y M D
798         function SQLDate($fmt, $col=false) {
799                 if (!$col) {
800                         $col = $this->sysDate;
801                 }
802                 return $col; // child class implement
803         }
805         /**
806          * Should prepare the sql statement and return the stmt resource.
807          * For databases that do not support this, we return the $sql. To ensure
808          * compatibility with databases that do not support prepare:
809          *
810          *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
811          *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
812          *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
813          *
814          * @param sql   SQL to send to database
815          *
816          * @return return FALSE, or the prepared statement, or the original sql if
817          *         if the database does not support prepare.
818          *
819          */
820         function Prepare($sql) {
821                 return $sql;
822         }
824         /**
825          * Some databases, eg. mssql require a different function for preparing
826          * stored procedures. So we cannot use Prepare().
827          *
828          * Should prepare the stored procedure  and return the stmt resource.
829          * For databases that do not support this, we return the $sql. To ensure
830          * compatibility with databases that do not support prepare:
831          *
832          * @param sql   SQL to send to database
833          *
834          * @return return FALSE, or the prepared statement, or the original sql if
835          *         if the database does not support prepare.
836          *
837          */
838         function PrepareSP($sql,$param=true) {
839                 return $this->Prepare($sql,$param);
840         }
842         /**
843         * PEAR DB Compat
844         */
845         function Quote($s) {
846                 return $this->qstr($s,false);
847         }
849         /**
850          * Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
851          */
852         function QMagic($s) {
853                 return $this->qstr($s,get_magic_quotes_gpc());
854         }
856         function q(&$s) {
857                 //if (!empty($this->qNull && $s == 'null') {
858                 //      return $s;
859                 //}
860                 $s = $this->qstr($s,false);
861         }
863         /**
864         * PEAR DB Compat - do not use internally.
865         */
866         function ErrorNative() {
867                 return $this->ErrorNo();
868         }
871         /**
872          * PEAR DB Compat - do not use internally.
873          */
874         function nextId($seq_name) {
875                 return $this->GenID($seq_name);
876         }
878         /**
879          * Lock a row, will escalate and lock the table if row locking not supported
880          * will normally free the lock at the end of the transaction
881          *
882          * @param $table        name of table to lock
883          * @param $where        where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
884          */
885         function RowLock($table,$where,$col='1 as adodbignore') {
886                 return false;
887         }
889         function CommitLock($table) {
890                 return $this->CommitTrans();
891         }
893         function RollbackLock($table) {
894                 return $this->RollbackTrans();
895         }
897         /**
898         * PEAR DB Compat - do not use internally.
899         *
900         * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
901         * for easy porting :-)
902         *
903         * @param mode   The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
904         * @returns              The previous fetch mode
905         */
906         function SetFetchMode($mode) {
907                 $old = $this->fetchMode;
908                 $this->fetchMode = $mode;
910                 if ($old === false) {
911                         global $ADODB_FETCH_MODE;
912                         return $ADODB_FETCH_MODE;
913                 }
914                 return $old;
915         }
918         /**
919         * PEAR DB Compat - do not use internally.
920         */
921         function Query($sql, $inputarr=false) {
922                 $rs = $this->Execute($sql, $inputarr);
923                 if (!$rs && defined('ADODB_PEAR')) {
924                         return ADODB_PEAR_Error();
925                 }
926                 return $rs;
927         }
930         /**
931         * PEAR DB Compat - do not use internally
932         */
933         function LimitQuery($sql, $offset, $count, $params=false) {
934                 $rs = $this->SelectLimit($sql, $count, $offset, $params);
935                 if (!$rs && defined('ADODB_PEAR')) {
936                         return ADODB_PEAR_Error();
937                 }
938                 return $rs;
939         }
942         /**
943         * PEAR DB Compat - do not use internally
944         */
945         function Disconnect() {
946                 return $this->Close();
947         }
949         /**
950          * Returns a placeholder for query parameters
951          * e.g. $DB->Param('a') will return
952          * - '?' for most databases
953          * - ':a' for Oracle
954          * - '$1', '$2', etc. for PostgreSQL
955          * @param string $name parameter's name, false to force a reset of the
956          *                     number to 1 (for databases that require positioned
957          *                     params such as PostgreSQL; note that ADOdb will
958          *                     automatically reset this when executing a query )
959          * @param string $type (unused)
960          * @return string query parameter placeholder
961          */
962         function Param($name,$type='C') {
963                 return '?';
964         }
966         /*
967                 InParameter and OutParameter are self-documenting versions of Parameter().
968         */
969         function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
970                 return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
971         }
973         /*
974         */
975         function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
976                 return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
978         }
981         /*
982         Usage in oracle
983                 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
984                 $db->Parameter($stmt,$id,'myid');
985                 $db->Parameter($stmt,$group,'group',64);
986                 $db->Execute();
988                 @param $stmt Statement returned by Prepare() or PrepareSP().
989                 @param $var PHP variable to bind to
990                 @param $name Name of stored procedure variable name to bind to.
991                 @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
992                 @param [$maxLen] Holds an maximum length of the variable.
993                 @param [$type] The data type of $var. Legal values depend on driver.
995         */
996         function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) {
997                 return false;
998         }
1001         function IgnoreErrors($saveErrs=false) {
1002                 if (!$saveErrs) {
1003                         $saveErrs = array($this->raiseErrorFn,$this->_transOK);
1004                         $this->raiseErrorFn = false;
1005                         return $saveErrs;
1006                 } else {
1007                         $this->raiseErrorFn = $saveErrs[0];
1008                         $this->_transOK = $saveErrs[1];
1009                 }
1010         }
1012         /**
1013          * Improved method of initiating a transaction. Used together with CompleteTrans().
1014          * Advantages include:
1015      *
1016          * a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
1017          *    Only the outermost block is treated as a transaction.<br>
1018          * b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
1019          * c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
1020          *    are disabled, making it backward compatible.
1021          */
1022         function StartTrans($errfn = 'ADODB_TransMonitor') {
1023                 if ($this->transOff > 0) {
1024                         $this->transOff += 1;
1025                         return true;
1026                 }
1028                 $this->_oldRaiseFn = $this->raiseErrorFn;
1029                 $this->raiseErrorFn = $errfn;
1030                 $this->_transOK = true;
1032                 if ($this->debug && $this->transCnt > 0) {
1033                         ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
1034                 }
1035                 $ok = $this->BeginTrans();
1036                 $this->transOff = 1;
1037                 return $ok;
1038         }
1041         /**
1042                 Used together with StartTrans() to end a transaction. Monitors connection
1043                 for sql errors, and will commit or rollback as appropriate.
1045                 @autoComplete if true, monitor sql errors and commit and rollback as appropriate,
1046                 and if set to false force rollback even if no SQL error detected.
1047                 @returns true on commit, false on rollback.
1048         */
1049         function CompleteTrans($autoComplete = true) {
1050                 if ($this->transOff > 1) {
1051                         $this->transOff -= 1;
1052                         return true;
1053                 }
1054                 $this->raiseErrorFn = $this->_oldRaiseFn;
1056                 $this->transOff = 0;
1057                 if ($this->_transOK && $autoComplete) {
1058                         if (!$this->CommitTrans()) {
1059                                 $this->_transOK = false;
1060                                 if ($this->debug) {
1061                                         ADOConnection::outp("Smart Commit failed");
1062                                 }
1063                         } else {
1064                                 if ($this->debug) {
1065                                         ADOConnection::outp("Smart Commit occurred");
1066                                 }
1067                         }
1068                 } else {
1069                         $this->_transOK = false;
1070                         $this->RollbackTrans();
1071                         if ($this->debug) {
1072                                 ADOCOnnection::outp("Smart Rollback occurred");
1073                         }
1074                 }
1076                 return $this->_transOK;
1077         }
1079         /*
1080                 At the end of a StartTrans/CompleteTrans block, perform a rollback.
1081         */
1082         function FailTrans() {
1083                 if ($this->debug)
1084                         if ($this->transOff == 0) {
1085                                 ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
1086                         } else {
1087                                 ADOConnection::outp("FailTrans was called");
1088                                 adodb_backtrace();
1089                         }
1090                 $this->_transOK = false;
1091         }
1093         /**
1094                 Check if transaction has failed, only for Smart Transactions.
1095         */
1096         function HasFailedTrans() {
1097                 if ($this->transOff > 0) {
1098                         return $this->_transOK == false;
1099                 }
1100                 return false;
1101         }
1103         /**
1104          * Execute SQL
1105          *
1106          * @param sql           SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
1107          * @param [inputarr]    holds the input data to bind to. Null elements will be set to null.
1108          * @return RecordSet or false
1109          */
1110         function Execute($sql,$inputarr=false) {
1111                 if ($this->fnExecute) {
1112                         $fn = $this->fnExecute;
1113                         $ret = $fn($this,$sql,$inputarr);
1114                         if (isset($ret)) {
1115                                 return $ret;
1116                         }
1117                 }
1118                 if ($inputarr !== false) {
1119                         if (!is_array($inputarr)) {
1120                                 $inputarr = array($inputarr);
1121                         }
1123                         $element0 = reset($inputarr);
1124                         # is_object check because oci8 descriptors can be passed in
1125                         $array_2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
1127                         //remove extra memory copy of input -mikefedyk
1128                         unset($element0);
1130                         if (!is_array($sql) && !$this->_bindInputArray) {
1131                                 // @TODO this would consider a '?' within a string as a parameter...
1132                                 $sqlarr = explode('?',$sql);
1133                                 $nparams = sizeof($sqlarr)-1;
1135                                 if (!$array_2d) {
1136                                         // When not Bind Bulk - convert to array of arguments list
1137                                         $inputarr = array($inputarr);
1138                                 } else {
1139                                         // Bulk bind - Make sure all list of params have the same number of elements
1140                                         $countElements = array_map('count', $inputarr);
1141                                         if (1 != count(array_unique($countElements))) {
1142                                                 $this->outp_throw(
1143                                                         "[bulk execute] Input array has different number of params  [" . print_r($countElements, true) .  "].",
1144                                                         'Execute'
1145                                                 );
1146                                                 return false;
1147                                         }
1148                                         unset($countElements);
1149                                 }
1150                                 // Make sure the number of parameters provided in the input
1151                                 // array matches what the query expects
1152                                 $element0 = reset($inputarr);
1153                                 if ($nparams != count($element0)) {
1154                                         $this->outp_throw(
1155                                                 "Input array has " . count($element0) .
1156                                                 " params, does not match query: '" . htmlspecialchars($sql) . "'",
1157                                                 'Execute'
1158                                         );
1159                                         return false;
1160                                 }
1162                                 // clean memory
1163                                 unset($element0);
1165                                 foreach($inputarr as $arr) {
1166                                         $sql = ''; $i = 0;
1167                                         foreach ($arr as $v) {
1168                                                 $sql .= $sqlarr[$i];
1169                                                 // from Ron Baldwin <ron.baldwin#sourceprose.com>
1170                                                 // Only quote string types
1171                                                 $typ = gettype($v);
1172                                                 if ($typ == 'string') {
1173                                                         //New memory copy of input created here -mikefedyk
1174                                                         $sql .= $this->qstr($v);
1175                                                 } else if ($typ == 'double') {
1176                                                         $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
1177                                                 } else if ($typ == 'boolean') {
1178                                                         $sql .= $v ? $this->true : $this->false;
1179                                                 } else if ($typ == 'object') {
1180                                                         if (method_exists($v, '__toString')) {
1181                                                                 $sql .= $this->qstr($v->__toString());
1182                                                         } else {
1183                                                                 $sql .= $this->qstr((string) $v);
1184                                                         }
1185                                                 } else if ($v === null) {
1186                                                         $sql .= 'NULL';
1187                                                 } else {
1188                                                         $sql .= $v;
1189                                                 }
1190                                                 $i += 1;
1192                                                 if ($i == $nparams) {
1193                                                         break;
1194                                                 }
1195                                         } // while
1196                                         if (isset($sqlarr[$i])) {
1197                                                 $sql .= $sqlarr[$i];
1198                                                 if ($i+1 != sizeof($sqlarr)) {
1199                                                         $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
1200                                                 }
1201                                         } else if ($i != sizeof($sqlarr)) {
1202                                                 $this->outp_throw( "Input array does not match ?: ".htmlspecialchars($sql),'Execute');
1203                                         }
1205                                         $ret = $this->_Execute($sql);
1206                                         if (!$ret) {
1207                                                 return $ret;
1208                                         }
1209                                 }
1210                         } else {
1211                                 if ($array_2d) {
1212                                         if (is_string($sql)) {
1213                                                 $stmt = $this->Prepare($sql);
1214                                         } else {
1215                                                 $stmt = $sql;
1216                                         }
1218                                         foreach($inputarr as $arr) {
1219                                                 $ret = $this->_Execute($stmt,$arr);
1220                                                 if (!$ret) {
1221                                                         return $ret;
1222                                                 }
1223                                         }
1224                                 } else {
1225                                         $ret = $this->_Execute($sql,$inputarr);
1226                                 }
1227                         }
1228                 } else {
1229                         $ret = $this->_Execute($sql,false);
1230                 }
1232                 return $ret;
1233         }
1235         function _Execute($sql,$inputarr=false) {
1236                 // ExecuteCursor() may send non-string queries (such as arrays),
1237                 // so we need to ignore those.
1238                 if( is_string($sql) ) {
1239                         // Strips keyword used to help generate SELECT COUNT(*) queries
1240                         // from SQL if it exists.
1241                         $sql = ADODB_str_replace( '_ADODB_COUNT', '', $sql );
1242                 }
1244                 if ($this->debug) {
1245                         global $ADODB_INCLUDED_LIB;
1246                         if (empty($ADODB_INCLUDED_LIB)) {
1247                                 include(ADODB_DIR.'/adodb-lib.inc.php');
1248                         }
1249                         $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
1250                 } else {
1251                         $this->_queryID = @$this->_query($sql,$inputarr);
1252                 }
1254                 // ************************
1255                 // OK, query executed
1256                 // ************************
1258                 // error handling if query fails
1259                 if ($this->_queryID === false) {
1260                         if ($this->debug == 99) {
1261                                 adodb_backtrace(true,5);
1262                         }
1263                         $fn = $this->raiseErrorFn;
1264                         if ($fn) {
1265                                 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
1266                         }
1267                         return false;
1268                 }
1270                 // return simplified recordset for inserts/updates/deletes with lower overhead
1271                 if ($this->_queryID === true) {
1272                         $rsclass = $this->rsPrefix.'empty';
1273                         $rs = (class_exists($rsclass)) ? new $rsclass():  new ADORecordSet_empty();
1275                         return $rs;
1276                 }
1278                 // return real recordset from select statement
1279                 $rsclass = $this->rsPrefix.$this->databaseType;
1280                 $rs = new $rsclass($this->_queryID,$this->fetchMode);
1281                 $rs->connection = $this; // Pablo suggestion
1282                 $rs->Init();
1283                 if (is_array($sql)) {
1284                         $rs->sql = $sql[0];
1285                 } else {
1286                         $rs->sql = $sql;
1287                 }
1288                 if ($rs->_numOfRows <= 0) {
1289                         global $ADODB_COUNTRECS;
1290                         if ($ADODB_COUNTRECS) {
1291                                 if (!$rs->EOF) {
1292                                         $rs = $this->_rs2rs($rs,-1,-1,!is_array($sql));
1293                                         $rs->_queryID = $this->_queryID;
1294                                 } else
1295                                         $rs->_numOfRows = 0;
1296                         }
1297                 }
1298                 return $rs;
1299         }
1301         function CreateSequence($seqname='adodbseq',$startID=1) {
1302                 if (empty($this->_genSeqSQL)) {
1303                         return false;
1304                 }
1305                 return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1306         }
1308         function DropSequence($seqname='adodbseq') {
1309                 if (empty($this->_dropSeqSQL)) {
1310                         return false;
1311                 }
1312                 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
1313         }
1315         /**
1316          * Generates a sequence id and stores it in $this->genID;
1317          * GenID is only available if $this->hasGenID = true;
1318          *
1319          * @param seqname               name of sequence to use
1320          * @param startID               if sequence does not exist, start at this ID
1321          * @return              0 if not supported, otherwise a sequence id
1322          */
1323         function GenID($seqname='adodbseq',$startID=1) {
1324                 if (!$this->hasGenID) {
1325                         return 0; // formerly returns false pre 1.60
1326                 }
1328                 $getnext = sprintf($this->_genIDSQL,$seqname);
1330                 $holdtransOK = $this->_transOK;
1332                 $save_handler = $this->raiseErrorFn;
1333                 $this->raiseErrorFn = '';
1334                 @($rs = $this->Execute($getnext));
1335                 $this->raiseErrorFn = $save_handler;
1337                 if (!$rs) {
1338                         $this->_transOK = $holdtransOK; //if the status was ok before reset
1339                         $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1340                         $rs = $this->Execute($getnext);
1341                 }
1342                 if ($rs && !$rs->EOF) {
1343                         $this->genID = reset($rs->fields);
1344                 } else {
1345                         $this->genID = 0; // false
1346                 }
1348                 if ($rs) {
1349                         $rs->Close();
1350                 }
1352                 return $this->genID;
1353         }
1355         /**
1356          * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
1357          * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
1358          * @return  the last inserted ID. Not all databases support this.
1359          */
1360         function Insert_ID($table='',$column='') {
1361                 if ($this->_logsql && $this->lastInsID) {
1362                         return $this->lastInsID;
1363                 }
1364                 if ($this->hasInsertID) {
1365                         return $this->_insertid($table,$column);
1366                 }
1367                 if ($this->debug) {
1368                         ADOConnection::outp( '<p>Insert_ID error</p>');
1369                         adodb_backtrace();
1370                 }
1371                 return false;
1372         }
1375         /**
1376          * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
1377          *
1378          * @return  the last inserted ID. All databases support this. But aware possible
1379          * problems in multiuser environments. Heavy test this before deploying.
1380          */
1381         function PO_Insert_ID($table="", $id="") {
1382                 if ($this->hasInsertID){
1383                         return $this->Insert_ID($table,$id);
1384                 } else {
1385                         return $this->GetOne("SELECT MAX($id) FROM $table");
1386                 }
1387         }
1389         /**
1390         * @return # rows affected by UPDATE/DELETE
1391         */
1392         function Affected_Rows() {
1393                 if ($this->hasAffectedRows) {
1394                         if ($this->fnExecute === 'adodb_log_sql') {
1395                                 if ($this->_logsql && $this->_affected !== false) {
1396                                         return $this->_affected;
1397                                 }
1398                         }
1399                         $val = $this->_affectedrows();
1400                         return ($val < 0) ? false : $val;
1401                 }
1403                 if ($this->debug) {
1404                         ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1405                 }
1406                 return false;
1407         }
1410         /**
1411          * @return  the last error message
1412          */
1413         function ErrorMsg() {
1414                 if ($this->_errorMsg) {
1415                         return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1416                 } else {
1417                         return '';
1418                 }
1419         }
1422         /**
1423          * @return the last error number. Normally 0 means no error.
1424          */
1425         function ErrorNo() {
1426                 return ($this->_errorMsg) ? -1 : 0;
1427         }
1429         function MetaError($err=false) {
1430                 include_once(ADODB_DIR."/adodb-error.inc.php");
1431                 if ($err === false) {
1432                         $err = $this->ErrorNo();
1433                 }
1434                 return adodb_error($this->dataProvider,$this->databaseType,$err);
1435         }
1437         function MetaErrorMsg($errno) {
1438                 include_once(ADODB_DIR."/adodb-error.inc.php");
1439                 return adodb_errormsg($errno);
1440         }
1442         /**
1443          * @returns an array with the primary key columns in it.
1444          */
1445         function MetaPrimaryKeys($table, $owner=false) {
1446         // owner not used in base class - see oci8
1447                 $p = array();
1448                 $objs = $this->MetaColumns($table);
1449                 if ($objs) {
1450                         foreach($objs as $v) {
1451                                 if (!empty($v->primary_key)) {
1452                                         $p[] = $v->name;
1453                                 }
1454                         }
1455                 }
1456                 if (sizeof($p)) {
1457                         return $p;
1458                 }
1459                 if (function_exists('ADODB_VIEW_PRIMARYKEYS')) {
1460                         return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1461                 }
1462                 return false;
1463         }
1465         /**
1466          * @returns assoc array where keys are tables, and values are foreign keys
1467          */
1468         function MetaForeignKeys($table, $owner=false, $upper=false) {
1469                 return false;
1470         }
1471         /**
1472          * Choose a database to connect to. Many databases do not support this.
1473          *
1474          * @param dbName is the name of the database to select
1475          * @return true or false
1476          */
1477         function SelectDB($dbName) {return false;}
1480         /**
1481          * Will select, getting rows from $offset (1-based), for $nrows.
1482          * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1483          * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1484          * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1485          * eg.
1486          *  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1487          *  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1488          *
1489          * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1490          * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1491          *
1492          * @param sql
1493          * @param [offset]      is the row to start calculations from (1-based)
1494          * @param [nrows]               is the number of rows to get
1495          * @param [inputarr]    array of bind variables
1496          * @param [secs2cache]          is a private parameter only used by jlim
1497          * @return              the recordset ($rs->databaseType == 'array')
1498          */
1499         function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) {
1500                 if ($this->hasTop && $nrows > 0) {
1501                         // suggested by Reinhard Balling. Access requires top after distinct
1502                         // Informix requires first before distinct - F Riosa
1503                         $ismssql = (strpos($this->databaseType,'mssql') !== false);
1504                         if ($ismssql) {
1505                                 $isaccess = false;
1506                         } else {
1507                                 $isaccess = (strpos($this->databaseType,'access') !== false);
1508                         }
1510                         if ($offset <= 0) {
1511                                         // access includes ties in result
1512                                         if ($isaccess) {
1513                                                 $sql = preg_replace(
1514                                                 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1516                                                 if ($secs2cache != 0) {
1517                                                         $ret = $this->CacheExecute($secs2cache, $sql,$inputarr);
1518                                                 } else {
1519                                                         $ret = $this->Execute($sql,$inputarr);
1520                                                 }
1521                                                 return $ret; // PHP5 fix
1522                                         } else if ($ismssql){
1523                                                 $sql = preg_replace(
1524                                                 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1525                                         } else {
1526                                                 $sql = preg_replace(
1527                                                 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1528                                         }
1529                         } else {
1530                                 $nn = $nrows + $offset;
1531                                 if ($isaccess || $ismssql) {
1532                                         $sql = preg_replace(
1533                                         '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1534                                 } else {
1535                                         $sql = preg_replace(
1536                                         '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1537                                 }
1538                         }
1539                 }
1541                 // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
1542                 // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1543                 global $ADODB_COUNTRECS;
1545                 $savec = $ADODB_COUNTRECS;
1546                 $ADODB_COUNTRECS = false;
1549                 if ($secs2cache != 0) {
1550                         $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1551                 } else {
1552                         $rs = $this->Execute($sql,$inputarr);
1553                 }
1555                 $ADODB_COUNTRECS = $savec;
1556                 if ($rs && !$rs->EOF) {
1557                         $rs = $this->_rs2rs($rs,$nrows,$offset);
1558                 }
1559                 //print_r($rs);
1560                 return $rs;
1561         }
1563         /**
1564         * Create serializable recordset. Breaks rs link to connection.
1565         *
1566         * @param rs                     the recordset to serialize
1567         */
1568         function SerializableRS(&$rs) {
1569                 $rs2 = $this->_rs2rs($rs);
1570                 $ignore = false;
1571                 $rs2->connection = $ignore;
1573                 return $rs2;
1574         }
1576         /**
1577         * Convert database recordset to an array recordset
1578         * input recordset's cursor should be at beginning, and
1579         * old $rs will be closed.
1580         *
1581         * @param rs                     the recordset to copy
1582         * @param [nrows]        number of rows to retrieve (optional)
1583         * @param [offset]       offset by number of rows (optional)
1584         * @return                       the new recordset
1585         */
1586         function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) {
1587                 if (! $rs) {
1588                         return false;
1589                 }
1590                 $dbtype = $rs->databaseType;
1591                 if (!$dbtype) {
1592                         $rs = $rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1593                         return $rs;
1594                 }
1595                 if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1596                         $rs->MoveFirst();
1597                         $rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1598                         return $rs;
1599                 }
1600                 $flds = array();
1601                 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1602                         $flds[] = $rs->FetchField($i);
1603                 }
1605                 $arr = $rs->GetArrayLimit($nrows,$offset);
1606                 //print_r($arr);
1607                 if ($close) {
1608                         $rs->Close();
1609                 }
1611                 $arrayClass = $this->arrayClass;
1613                 $rs2 = new $arrayClass();
1614                 $rs2->connection = $this;
1615                 $rs2->sql = $rs->sql;
1616                 $rs2->dataProvider = $this->dataProvider;
1617                 $rs2->InitArrayFields($arr,$flds);
1618                 $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1619                 return $rs2;
1620         }
1622         /*
1623         * Return all rows. Compat with PEAR DB
1624         */
1625         function GetAll($sql, $inputarr=false) {
1626                 $arr = $this->GetArray($sql,$inputarr);
1627                 return $arr;
1628         }
1630         function GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false) {
1631                 $rs = $this->Execute($sql, $inputarr);
1632                 if (!$rs) {
1633                         return false;
1634                 }
1635                 $arr = $rs->GetAssoc($force_array,$first2cols);
1636                 return $arr;
1637         }
1639         function CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false) {
1640                 if (!is_numeric($secs2cache)) {
1641                         $first2cols = $force_array;
1642                         $force_array = $inputarr;
1643                 }
1644                 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
1645                 if (!$rs) {
1646                         return false;
1647                 }
1648                 $arr = $rs->GetAssoc($force_array,$first2cols);
1649                 return $arr;
1650         }
1652         /**
1653         * Return first element of first row of sql statement. Recordset is disposed
1654         * for you.
1655         *
1656         * @param sql                    SQL statement
1657         * @param [inputarr]             input bind array
1658         */
1659         function GetOne($sql,$inputarr=false) {
1660                 global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
1662                 $crecs = $ADODB_COUNTRECS;
1663                 $ADODB_COUNTRECS = false;
1665                 $ret = false;
1666                 $rs = $this->Execute($sql,$inputarr);
1667                 if ($rs) {
1668                         if ($rs->EOF) {
1669                                 $ret = $ADODB_GETONE_EOF;
1670                         } else {
1671                                 $ret = reset($rs->fields);
1672                         }
1674                         $rs->Close();
1675                 }
1676                 $ADODB_COUNTRECS = $crecs;
1677                 return $ret;
1678         }
1680         // $where should include 'WHERE fld=value'
1681         function GetMedian($table, $field,$where = '') {
1682                 $total = $this->GetOne("select count(*) from $table $where");
1683                 if (!$total) {
1684                         return false;
1685                 }
1687                 $midrow = (integer) ($total/2);
1688                 $rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
1689                 if ($rs && !$rs->EOF) {
1690                         return reset($rs->fields);
1691                 }
1692                 return false;
1693         }
1696         function CacheGetOne($secs2cache,$sql=false,$inputarr=false) {
1697                 global $ADODB_GETONE_EOF;
1699                 $ret = false;
1700                 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1701                 if ($rs) {
1702                         if ($rs->EOF) {
1703                                 $ret = $ADODB_GETONE_EOF;
1704                         } else {
1705                                 $ret = reset($rs->fields);
1706                         }
1707                         $rs->Close();
1708                 }
1710                 return $ret;
1711         }
1713         function GetCol($sql, $inputarr = false, $trim = false) {
1715                 $rs = $this->Execute($sql, $inputarr);
1716                 if ($rs) {
1717                         $rv = array();
1718                         if ($trim) {
1719                                 while (!$rs->EOF) {
1720                                         $rv[] = trim(reset($rs->fields));
1721                                         $rs->MoveNext();
1722                                 }
1723                         } else {
1724                                 while (!$rs->EOF) {
1725                                         $rv[] = reset($rs->fields);
1726                                         $rs->MoveNext();
1727                                 }
1728                         }
1729                         $rs->Close();
1730                 } else {
1731                         $rv = false;
1732                 }
1733                 return $rv;
1734         }
1736         function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) {
1737                 $rs = $this->CacheExecute($secs, $sql, $inputarr);
1738                 if ($rs) {
1739                         $rv = array();
1740                         if ($trim) {
1741                                 while (!$rs->EOF) {
1742                                         $rv[] = trim(reset($rs->fields));
1743                                         $rs->MoveNext();
1744                                 }
1745                         } else {
1746                                 while (!$rs->EOF) {
1747                                         $rv[] = reset($rs->fields);
1748                                         $rs->MoveNext();
1749                                 }
1750                         }
1751                         $rs->Close();
1752                 } else
1753                         $rv = false;
1755                 return $rv;
1756         }
1758         function Transpose(&$rs,$addfieldnames=true) {
1759                 $rs2 = $this->_rs2rs($rs);
1760                 if (!$rs2) {
1761                         return false;
1762                 }
1764                 $rs2->_transpose($addfieldnames);
1765                 return $rs2;
1766         }
1768         /*
1769                 Calculate the offset of a date for a particular database and generate
1770                         appropriate SQL. Useful for calculating future/past dates and storing
1771                         in a database.
1773                 If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1774         */
1775         function OffsetDate($dayFraction,$date=false) {
1776                 if (!$date) {
1777                         $date = $this->sysDate;
1778                 }
1779                 return  '('.$date.'+'.$dayFraction.')';
1780         }
1783         /**
1784         *
1785         * @param sql                    SQL statement
1786         * @param [inputarr]             input bind array
1787         */
1788         function GetArray($sql,$inputarr=false) {
1789                 global $ADODB_COUNTRECS;
1791                 $savec = $ADODB_COUNTRECS;
1792                 $ADODB_COUNTRECS = false;
1793                 $rs = $this->Execute($sql,$inputarr);
1794                 $ADODB_COUNTRECS = $savec;
1795                 if (!$rs)
1796                         if (defined('ADODB_PEAR')) {
1797                                 $cls = ADODB_PEAR_Error();
1798                                 return $cls;
1799                         } else {
1800                                 return false;
1801                         }
1802                 $arr = $rs->GetArray();
1803                 $rs->Close();
1804                 return $arr;
1805         }
1807         function CacheGetAll($secs2cache,$sql=false,$inputarr=false) {
1808                 $arr = $this->CacheGetArray($secs2cache,$sql,$inputarr);
1809                 return $arr;
1810         }
1812         function CacheGetArray($secs2cache,$sql=false,$inputarr=false) {
1813                 global $ADODB_COUNTRECS;
1815                 $savec = $ADODB_COUNTRECS;
1816                 $ADODB_COUNTRECS = false;
1817                 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1818                 $ADODB_COUNTRECS = $savec;
1820                 if (!$rs)
1821                         if (defined('ADODB_PEAR')) {
1822                                 $cls = ADODB_PEAR_Error();
1823                                 return $cls;
1824                         } else {
1825                                 return false;
1826                         }
1827                 $arr = $rs->GetArray();
1828                 $rs->Close();
1829                 return $arr;
1830         }
1832         function GetRandRow($sql, $arr= false) {
1833                 $rezarr = $this->GetAll($sql, $arr);
1834                 $sz = sizeof($rezarr);
1835                 return $rezarr[abs(rand()) % $sz];
1836         }
1838         /**
1839         * Return one row of sql statement. Recordset is disposed for you.
1840         * Note that SelectLimit should not be called.
1841         *
1842         * @param sql                    SQL statement
1843         * @param [inputarr]             input bind array
1844         */
1845         function GetRow($sql,$inputarr=false) {
1846                 global $ADODB_COUNTRECS;
1848                 $crecs = $ADODB_COUNTRECS;
1849                 $ADODB_COUNTRECS = false;
1851                 $rs = $this->Execute($sql,$inputarr);
1853                 $ADODB_COUNTRECS = $crecs;
1854                 if ($rs) {
1855                         if (!$rs->EOF) {
1856                                 $arr = $rs->fields;
1857                         } else {
1858                                 $arr = array();
1859                         }
1860                         $rs->Close();
1861                         return $arr;
1862                 }
1864                 return false;
1865         }
1867         function CacheGetRow($secs2cache,$sql=false,$inputarr=false) {
1868                 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1869                 if ($rs) {
1870                         if (!$rs->EOF) {
1871                                 $arr = $rs->fields;
1872                         } else {
1873                                 $arr = array();
1874                         }
1876                         $rs->Close();
1877                         return $arr;
1878                 }
1879                 return false;
1880         }
1882         /**
1883         * Insert or replace a single record. Note: this is not the same as MySQL's replace.
1884         * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1885         * Also note that no table locking is done currently, so it is possible that the
1886         * record be inserted twice by two programs...
1887         *
1888         * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1889         *
1890         * $table                table name
1891         * $fieldArray   associative array of data (you must quote strings yourself).
1892         * $keyCol               the primary key field name or if compound key, array of field names
1893         * autoQuote             set to true to use a hueristic to quote strings. Works with nulls and numbers
1894         *                                       but does not work with dates nor SQL functions.
1895         * has_autoinc   the primary key is an auto-inc field, so skip in insert.
1896         *
1897         * Currently blob replace not supported
1898         *
1899         * returns 0 = fail, 1 = update, 2 = insert
1900         */
1902         function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) {
1903                 global $ADODB_INCLUDED_LIB;
1904                 if (empty($ADODB_INCLUDED_LIB)) {
1905                         include(ADODB_DIR.'/adodb-lib.inc.php');
1906                 }
1908                 return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1909         }
1912         /**
1913          * Will select, getting rows from $offset (1-based), for $nrows.
1914          * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1915          * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1916          * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1917          * eg.
1918          *  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1919          *  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1920          *
1921          * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1922          *
1923          * @param [secs2cache]  seconds to cache data, set to 0 to force query. This is optional
1924          * @param sql
1925          * @param [offset]      is the row to start calculations from (1-based)
1926          * @param [nrows]       is the number of rows to get
1927          * @param [inputarr]    array of bind variables
1928          * @return              the recordset ($rs->databaseType == 'array')
1929          */
1930         function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) {
1931                 if (!is_numeric($secs2cache)) {
1932                         if ($sql === false) {
1933                                 $sql = -1;
1934                         }
1935                         if ($offset == -1) {
1936                                 $offset = false;
1937                         }
1938                                                                                                 // sql, nrows, offset,inputarr
1939                         $rs = $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1940                 } else {
1941                         if ($sql === false) {
1942                                 $this->outp_throw("Warning: \$sql missing from CacheSelectLimit()",'CacheSelectLimit');
1943                         }
1944                         $rs = $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1945                 }
1946                 return $rs;
1947         }
1949         /**
1950          * Flush cached recordsets that match a particular $sql statement.
1951          * If $sql == false, then we purge all files in the cache.
1952          */
1953         function CacheFlush($sql=false,$inputarr=false) {
1954                 global $ADODB_CACHE_DIR, $ADODB_CACHE;
1956                 # Create cache if it does not exist
1957                 if (empty($ADODB_CACHE)) {
1958                         $this->_CreateCache();
1959                 }
1961                 if (!$sql) {
1962                         $ADODB_CACHE->flushall($this->debug);
1963                         return;
1964                 }
1966                 $f = $this->_gencachename($sql.serialize($inputarr),false);
1967                 return $ADODB_CACHE->flushcache($f, $this->debug);
1968         }
1971         /**
1972          * Private function to generate filename for caching.
1973          * Filename is generated based on:
1974          *
1975          *  - sql statement
1976          *  - database type (oci8, ibase, ifx, etc)
1977          *  - database name
1978          *  - userid
1979          *  - setFetchMode (adodb 4.23)
1980          *
1981          * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
1982          * Assuming that we can have 50,000 files per directory with good performance,
1983          * then we can scale to 12.8 million unique cached recordsets. Wow!
1984          */
1985         function _gencachename($sql,$createdir) {
1986                 global $ADODB_CACHE, $ADODB_CACHE_DIR;
1988                 if ($this->fetchMode === false) {
1989                         global $ADODB_FETCH_MODE;
1990                         $mode = $ADODB_FETCH_MODE;
1991                 } else {
1992                         $mode = $this->fetchMode;
1993                 }
1994                 $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1995                 if (!$ADODB_CACHE->createdir) {
1996                         return $m;
1997                 }
1998                 if (!$createdir) {
1999                         $dir = $ADODB_CACHE->getdirname($m);
2000                 } else {
2001                         $dir = $ADODB_CACHE->createdir($m, $this->debug);
2002                 }
2004                 return $dir.'/adodb_'.$m.'.cache';
2005         }
2008         /**
2009          * Execute SQL, caching recordsets.
2010          *
2011          * @param [secs2cache]  seconds to cache data, set to 0 to force query.
2012          *                                        This is an optional parameter.
2013          * @param sql           SQL statement to execute
2014          * @param [inputarr]    holds the input data  to bind to
2015          * @return              RecordSet or false
2016          */
2017         function CacheExecute($secs2cache,$sql=false,$inputarr=false) {
2018                 global $ADODB_CACHE;
2020                 if (empty($ADODB_CACHE)) {
2021                         $this->_CreateCache();
2022                 }
2024                 if (!is_numeric($secs2cache)) {
2025                         $inputarr = $sql;
2026                         $sql = $secs2cache;
2027                         $secs2cache = $this->cacheSecs;
2028                 }
2030                 if (is_array($sql)) {
2031                         $sqlparam = $sql;
2032                         $sql = $sql[0];
2033                 } else
2034                         $sqlparam = $sql;
2037                 $md5file = $this->_gencachename($sql.serialize($inputarr),true);
2038                 $err = '';
2040                 if ($secs2cache > 0){
2041                         $rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
2042                         $this->numCacheHits += 1;
2043                 } else {
2044                         $err='Timeout 1';
2045                         $rs = false;
2046                         $this->numCacheMisses += 1;
2047                 }
2049                 if (!$rs) {
2050                 // no cached rs found
2051                         if ($this->debug) {
2052                                 if (get_magic_quotes_runtime() && !$this->memCache) {
2053                                         ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
2054                                 }
2055                                 if ($this->debug !== -1) {
2056                                         ADOConnection::outp( " $md5file cache failure: $err (this is a notice and not an error)");
2057                                 }
2058                         }
2060                         $rs = $this->Execute($sqlparam,$inputarr);
2062                         if ($rs) {
2063                                 $eof = $rs->EOF;
2064                                 $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
2065                                 $rs->timeCreated = time(); // used by caching
2066                                 $txt = _rs2serialize($rs,false,$sql); // serialize
2068                                 $ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
2069                                 if (!$ok) {
2070                                         if ($ok === false) {
2071                                                 $em = 'Cache write error';
2072                                                 $en = -32000;
2074                                                 if ($fn = $this->raiseErrorFn) {
2075                                                         $fn($this->databaseType,'CacheExecute', $en, $em, $md5file,$sql,$this);
2076                                                 }
2077                                         } else {
2078                                                 $em = 'Cache file locked warning';
2079                                                 $en = -32001;
2080                                                 // do not call error handling for just a warning
2081                                         }
2083                                         if ($this->debug) {
2084                                                 ADOConnection::outp( " ".$em);
2085                                         }
2086                                 }
2087                                 if ($rs->EOF && !$eof) {
2088                                         $rs->MoveFirst();
2089                                         //$rs = csv2rs($md5file,$err);
2090                                         $rs->connection = $this; // Pablo suggestion
2091                                 }
2093                         } else if (!$this->memCache) {
2094                                 $ADODB_CACHE->flushcache($md5file);
2095                         }
2096                 } else {
2097                         $this->_errorMsg = '';
2098                         $this->_errorCode = 0;
2100                         if ($this->fnCacheExecute) {
2101                                 $fn = $this->fnCacheExecute;
2102                                 $fn($this, $secs2cache, $sql, $inputarr);
2103                         }
2104                         // ok, set cached object found
2105                         $rs->connection = $this; // Pablo suggestion
2106                         if ($this->debug){
2107                                 if ($this->debug == 99) {
2108                                         adodb_backtrace();
2109                                 }
2110                                 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
2111                                 $ttl = $rs->timeCreated + $secs2cache - time();
2112                                 $s = is_array($sql) ? $sql[0] : $sql;
2113                                 if ($inBrowser) {
2114                                         $s = '<i>'.htmlspecialchars($s).'</i>';
2115                                 }
2117                                 ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
2118                         }
2119                 }
2120                 return $rs;
2121         }
2124         /*
2125                 Similar to PEAR DB's autoExecute(), except that
2126                 $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
2127                 If $mode == 'UPDATE', then $where is compulsory as a safety measure.
2129                 $forceUpdate means that even if the data has not changed, perform update.
2130          */
2131         function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = false, $forceUpdate = true, $magicq = false) {
2132                 if ($where === false && ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) ) {
2133                         $this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause', 'AutoExecute');
2134                         return false;
2135                 }
2137                 $sql = "SELECT * FROM $table";
2138                 $rs = $this->SelectLimit($sql, 1);
2139                 if (!$rs) {
2140                         return false; // table does not exist
2141                 }
2143                 $rs->tableName = $table;
2144                 if ($where !== false) {
2145                         $sql .= " WHERE $where";
2146                 }
2147                 $rs->sql = $sql;
2149                 switch($mode) {
2150                         case 'UPDATE':
2151                         case DB_AUTOQUERY_UPDATE:
2152                                 $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
2153                                 break;
2154                         case 'INSERT':
2155                         case DB_AUTOQUERY_INSERT:
2156                                 $sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
2157                                 break;
2158                         default:
2159                                 $this->outp_throw("AutoExecute: Unknown mode=$mode", 'AutoExecute');
2160                                 return false;
2161                 }
2162                 return $sql && $this->Execute($sql);
2163         }
2166         /**
2167          * Generates an Update Query based on an existing recordset.
2168          * $arrFields is an associative array of fields with the value
2169          * that should be assigned.
2170          *
2171          * Note: This function should only be used on a recordset
2172          *         that is run against a single table and sql should only
2173          *               be a simple select stmt with no groupby/orderby/limit
2174          *
2175          * "Jonathan Younger" <jyounger@unilab.com>
2176          */
2177         function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null) {
2178                 global $ADODB_INCLUDED_LIB;
2180                 // ********************************************************
2181                 // This is here to maintain compatibility
2182                 // with older adodb versions. Sets force type to force nulls if $forcenulls is set.
2183                 if (!isset($force)) {
2184                         global $ADODB_FORCE_TYPE;
2185                         $force = $ADODB_FORCE_TYPE;
2186                 }
2187                 // ********************************************************
2189                 if (empty($ADODB_INCLUDED_LIB)) {
2190                         include(ADODB_DIR.'/adodb-lib.inc.php');
2191                 }
2192                 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
2193         }
2195         /**
2196          * Generates an Insert Query based on an existing recordset.
2197          * $arrFields is an associative array of fields with the value
2198          * that should be assigned.
2199          *
2200          * Note: This function should only be used on a recordset
2201          *       that is run against a single table.
2202          */
2203         function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null) {
2204                 global $ADODB_INCLUDED_LIB;
2205                 if (!isset($force)) {
2206                         global $ADODB_FORCE_TYPE;
2207                         $force = $ADODB_FORCE_TYPE;
2208                 }
2209                 if (empty($ADODB_INCLUDED_LIB)) {
2210                         include(ADODB_DIR.'/adodb-lib.inc.php');
2211                 }
2212                 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
2213         }
2216         /**
2217         * Update a blob column, given a where clause. There are more sophisticated
2218         * blob handling functions that we could have implemented, but all require
2219         * a very complex API. Instead we have chosen something that is extremely
2220         * simple to understand and use.
2221         *
2222         * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
2223         *
2224         * Usage to update a $blobvalue which has a primary key blob_id=1 into a
2225         * field blobtable.blobcolumn:
2226         *
2227         *       UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
2228         *
2229         * Insert example:
2230         *
2231         *       $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2232         *       $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
2233         */
2234         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') {
2235                 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
2236         }
2238         /**
2239         * Usage:
2240         *       UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
2241         *
2242         *       $blobtype supports 'BLOB' and 'CLOB'
2243         *
2244         *       $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2245         *       $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
2246         */
2247         function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') {
2248                 $fd = fopen($path,'rb');
2249                 if ($fd === false) {
2250                         return false;
2251                 }
2252                 $val = fread($fd,filesize($path));
2253                 fclose($fd);
2254                 return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
2255         }
2257         function BlobDecode($blob) {
2258                 return $blob;
2259         }
2261         function BlobEncode($blob) {
2262                 return $blob;
2263         }
2265         function GetCharSet() {
2266                 return $this->charSet;
2267         }
2269         function SetCharSet($charset) {
2270                 $this->charSet = $charset;
2271                 return true;
2272         }
2274         function IfNull( $field, $ifNull ) {
2275                 return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
2276         }
2278         function LogSQL($enable=true) {
2279                 include_once(ADODB_DIR.'/adodb-perf.inc.php');
2281                 if ($enable) {
2282                         $this->fnExecute = 'adodb_log_sql';
2283                 } else {
2284                         $this->fnExecute = false;
2285                 }
2287                 $old = $this->_logsql;
2288                 $this->_logsql = $enable;
2289                 if ($enable && !$old) {
2290                         $this->_affected = false;
2291                 }
2292                 return $old;
2293         }
2295         /**
2296         * Usage:
2297         *       UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
2298         *
2299         *       $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
2300         *       $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
2301         */
2302         function UpdateClob($table,$column,$val,$where) {
2303                 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
2304         }
2306         // not the fastest implementation - quick and dirty - jlim
2307         // for best performance, use the actual $rs->MetaType().
2308         function MetaType($t,$len=-1,$fieldobj=false) {
2310                 if (empty($this->_metars)) {
2311                         $rsclass = $this->rsPrefix.$this->databaseType;
2312                         $this->_metars = new $rsclass(false,$this->fetchMode);
2313                         $this->_metars->connection = $this;
2314                 }
2315                 return $this->_metars->MetaType($t,$len,$fieldobj);
2316         }
2319         /**
2320         *  Change the SQL connection locale to a specified locale.
2321         *  This is used to get the date formats written depending on the client locale.
2322         */
2323         function SetDateLocale($locale = 'En') {
2324                 $this->locale = $locale;
2325                 switch (strtoupper($locale))
2326                 {
2327                         case 'EN':
2328                                 $this->fmtDate="'Y-m-d'";
2329                                 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2330                                 break;
2332                         case 'US':
2333                                 $this->fmtDate = "'m-d-Y'";
2334                                 $this->fmtTimeStamp = "'m-d-Y H:i:s'";
2335                                 break;
2337                         case 'PT_BR':
2338                         case 'NL':
2339                         case 'FR':
2340                         case 'RO':
2341                         case 'IT':
2342                                 $this->fmtDate="'d-m-Y'";
2343                                 $this->fmtTimeStamp = "'d-m-Y H:i:s'";
2344                                 break;
2346                         case 'GE':
2347                                 $this->fmtDate="'d.m.Y'";
2348                                 $this->fmtTimeStamp = "'d.m.Y H:i:s'";
2349                                 break;
2351                         default:
2352                                 $this->fmtDate="'Y-m-d'";
2353                                 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2354                                 break;
2355                 }
2356         }
2358         /**
2359          * GetActiveRecordsClass Performs an 'ALL' query
2360          *
2361          * @param mixed $class This string represents the class of the current active record
2362          * @param mixed $table Table used by the active record object
2363          * @param mixed $whereOrderBy Where, order, by clauses
2364          * @param mixed $bindarr
2365          * @param mixed $primkeyArr
2366          * @param array $extra Query extras: limit, offset...
2367          * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
2368          * @access public
2369          * @return void
2370          */
2371         function GetActiveRecordsClass(
2372                         $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
2373                         $extra=array(),
2374                         $relations=array())
2375         {
2376                 global $_ADODB_ACTIVE_DBS;
2377                 ## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
2378                 ## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
2379                 if (!isset($_ADODB_ACTIVE_DBS)) {
2380                         include_once(ADODB_DIR.'/adodb-active-record.inc.php');
2381                 }
2382                 return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
2383         }
2385         function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false) {
2386                 $arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2387                 return $arr;
2388         }
2390         /**
2391          * Close Connection
2392          */
2393         function Close() {
2394                 $rez = $this->_close();
2395                 $this->_connectionID = false;
2396                 return $rez;
2397         }
2399         /**
2400          * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2401          *
2402          * @return true if succeeded or false if database does not support transactions
2403          */
2404         function BeginTrans() {
2405                 if ($this->debug) {
2406                         ADOConnection::outp("BeginTrans: Transactions not supported for this driver");
2407                 }
2408                 return false;
2409         }
2411         /* set transaction mode */
2412         function SetTransactionMode( $transaction_mode ) {
2413                 $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2414                 $this->_transmode  = $transaction_mode;
2415         }
2416 /*
2417 http://msdn2.microsoft.com/en-US/ms173763.aspx
2418 http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2419 http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2420 http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2421 */
2422         function MetaTransaction($mode,$db) {
2423                 $mode = strtoupper($mode);
2424                 $mode = str_replace('ISOLATION LEVEL ','',$mode);
2426                 switch($mode) {
2428                 case 'READ UNCOMMITTED':
2429                         switch($db) {
2430                         case 'oci8':
2431                         case 'oracle':
2432                                 return 'ISOLATION LEVEL READ COMMITTED';
2433                         default:
2434                                 return 'ISOLATION LEVEL READ UNCOMMITTED';
2435                         }
2436                         break;
2438                 case 'READ COMMITTED':
2439                                 return 'ISOLATION LEVEL READ COMMITTED';
2440                         break;
2442                 case 'REPEATABLE READ':
2443                         switch($db) {
2444                         case 'oci8':
2445                         case 'oracle':
2446                                 return 'ISOLATION LEVEL SERIALIZABLE';
2447                         default:
2448                                 return 'ISOLATION LEVEL REPEATABLE READ';
2449                         }
2450                         break;
2452                 case 'SERIALIZABLE':
2453                                 return 'ISOLATION LEVEL SERIALIZABLE';
2454                         break;
2456                 default:
2457                         return $mode;
2458                 }
2459         }
2461         /**
2462          * If database does not support transactions, always return true as data always commited
2463          *
2464          * @param $ok  set to false to rollback transaction, true to commit
2465          *
2466          * @return true/false.
2467          */
2468         function CommitTrans($ok=true) {
2469                 return true;
2470         }
2473         /**
2474          * If database does not support transactions, rollbacks always fail, so return false
2475          *
2476          * @return true/false.
2477          */
2478         function RollbackTrans() {
2479                 return false;
2480         }
2483         /**
2484          * return the databases that the driver can connect to.
2485          * Some databases will return an empty array.
2486          *
2487          * @return an array of database names.
2488          */
2489         function MetaDatabases() {
2490                 global $ADODB_FETCH_MODE;
2492                 if ($this->metaDatabasesSQL) {
2493                         $save = $ADODB_FETCH_MODE;
2494                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2496                         if ($this->fetchMode !== false) {
2497                                 $savem = $this->SetFetchMode(false);
2498                         }
2500                         $arr = $this->GetCol($this->metaDatabasesSQL);
2501                         if (isset($savem)) {
2502                                 $this->SetFetchMode($savem);
2503                         }
2504                         $ADODB_FETCH_MODE = $save;
2506                         return $arr;
2507                 }
2509                 return false;
2510         }
2512         /**
2513          * List procedures or functions in an array.
2514          * @param procedureNamePattern  a procedure name pattern; must match the procedure name as it is stored in the database
2515          * @param catalog a catalog name; must match the catalog name as it is stored in the database;
2516          * @param schemaPattern a schema name pattern;
2517          *
2518          * @return array of procedures on current database.
2519          *
2520          * Array(
2521          *   [name_of_procedure] => Array(
2522          *     [type] => PROCEDURE or FUNCTION
2523          *     [catalog] => Catalog_name
2524          *     [schema] => Schema_name
2525          *     [remarks] => explanatory comment on the procedure
2526          *   )
2527          * )
2528          */
2529         function MetaProcedures($procedureNamePattern = null, $catalog  = null, $schemaPattern  = null) {
2530                 return false;
2531         }
2534         /**
2535          * @param ttype can either be 'VIEW' or 'TABLE' or false.
2536          *              If false, both views and tables are returned.
2537          *              "VIEW" returns only views
2538          *              "TABLE" returns only tables
2539          * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2540          * @param mask  is the input mask - only supported by oci8 and postgresql
2541          *
2542          * @return  array of tables for current database.
2543          */
2544         function MetaTables($ttype=false,$showSchema=false,$mask=false) {
2545                 global $ADODB_FETCH_MODE;
2547                 if ($mask) {
2548                         return false;
2549                 }
2550                 if ($this->metaTablesSQL) {
2551                         $save = $ADODB_FETCH_MODE;
2552                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2554                         if ($this->fetchMode !== false) {
2555                                 $savem = $this->SetFetchMode(false);
2556                         }
2558                         $rs = $this->Execute($this->metaTablesSQL);
2559                         if (isset($savem)) {
2560                                 $this->SetFetchMode($savem);
2561                         }
2562                         $ADODB_FETCH_MODE = $save;
2564                         if ($rs === false) {
2565                                 return false;
2566                         }
2567                         $arr = $rs->GetArray();
2568                         $arr2 = array();
2570                         if ($hast = ($ttype && isset($arr[0][1]))) {
2571                                 $showt = strncmp($ttype,'T',1);
2572                         }
2574                         for ($i=0; $i < sizeof($arr); $i++) {
2575                                 if ($hast) {
2576                                         if ($showt == 0) {
2577                                                 if (strncmp($arr[$i][1],'T',1) == 0) {
2578                                                         $arr2[] = trim($arr[$i][0]);
2579                                                 }
2580                                         } else {
2581                                                 if (strncmp($arr[$i][1],'V',1) == 0) {
2582                                                         $arr2[] = trim($arr[$i][0]);
2583                                                 }
2584                                         }
2585                                 } else
2586                                         $arr2[] = trim($arr[$i][0]);
2587                         }
2588                         $rs->Close();
2589                         return $arr2;
2590                 }
2591                 return false;
2592         }
2595         function _findschema(&$table,&$schema) {
2596                 if (!$schema && ($at = strpos($table,'.')) !== false) {
2597                         $schema = substr($table,0,$at);
2598                         $table = substr($table,$at+1);
2599                 }
2600         }
2602         /**
2603          * List columns in a database as an array of ADOFieldObjects.
2604          * See top of file for definition of object.
2605          *
2606          * @param $table        table name to query
2607          * @param $normalize    makes table name case-insensitive (required by some databases)
2608          * @schema is optional database schema to use - not supported by all databases.
2609          *
2610          * @return  array of ADOFieldObjects for current table.
2611          */
2612         function MetaColumns($table,$normalize=true) {
2613                 global $ADODB_FETCH_MODE;
2615                 if (!empty($this->metaColumnsSQL)) {
2616                         $schema = false;
2617                         $this->_findschema($table,$schema);
2619                         $save = $ADODB_FETCH_MODE;
2620                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2621                         if ($this->fetchMode !== false) {
2622                                 $savem = $this->SetFetchMode(false);
2623                         }
2624                         $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2625                         if (isset($savem)) {
2626                                 $this->SetFetchMode($savem);
2627                         }
2628                         $ADODB_FETCH_MODE = $save;
2629                         if ($rs === false || $rs->EOF) {
2630                                 return false;
2631                         }
2633                         $retarr = array();
2634                         while (!$rs->EOF) { //print_r($rs->fields);
2635                                 $fld = new ADOFieldObject();
2636                                 $fld->name = $rs->fields[0];
2637                                 $fld->type = $rs->fields[1];
2638                                 if (isset($rs->fields[3]) && $rs->fields[3]) {
2639                                         if ($rs->fields[3]>0) {
2640                                                 $fld->max_length = $rs->fields[3];
2641                                         }
2642                                         $fld->scale = $rs->fields[4];
2643                                         if ($fld->scale>0) {
2644                                                 $fld->max_length += 1;
2645                                         }
2646                                 } else {
2647                                         $fld->max_length = $rs->fields[2];
2648                                 }
2650                                 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) {
2651                                         $retarr[] = $fld;
2652                                 } else {
2653                                         $retarr[strtoupper($fld->name)] = $fld;
2654                                 }
2655                                 $rs->MoveNext();
2656                         }
2657                         $rs->Close();
2658                         return $retarr;
2659                 }
2660                 return false;
2661         }
2663         /**
2664          * List indexes on a table as an array.
2665          * @param table  table name to query
2666          * @param primary true to only show primary keys. Not actually used for most databases
2667          *
2668          * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2669          *
2670          * Array(
2671          *   [name_of_index] => Array(
2672          *     [unique] => true or false
2673          *     [columns] => Array(
2674          *       [0] => firstname
2675          *       [1] => lastname
2676          *     )
2677          *   )
2678          * )
2679          */
2680         function MetaIndexes($table, $primary = false, $owner = false) {
2681                 return false;
2682         }
2684         /**
2685          * List columns names in a table as an array.
2686          * @param table table name to query
2687          *
2688          * @return  array of column names for current table.
2689          */
2690         function MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) {
2691                 $objarr = $this->MetaColumns($table);
2692                 if (!is_array($objarr)) {
2693                         return false;
2694                 }
2695                 $arr = array();
2696                 if ($numIndexes) {
2697                         $i = 0;
2698                         if ($useattnum) {
2699                                 foreach($objarr as $v)
2700                                         $arr[$v->attnum] = $v->name;
2702                         } else
2703                                 foreach($objarr as $v) $arr[$i++] = $v->name;
2704                 } else
2705                         foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2707                 return $arr;
2708         }
2710         /**
2711          * Different SQL databases used different methods to combine strings together.
2712          * This function provides a wrapper.
2713          *
2714          * param s      variable number of string parameters
2715          *
2716          * Usage: $db->Concat($str1,$str2);
2717          *
2718          * @return concatenated string
2719          */
2720         function Concat() {
2721                 $arr = func_get_args();
2722                 return implode($this->concat_operator, $arr);
2723         }
2726         /**
2727          * Converts a date "d" to a string that the database can understand.
2728          *
2729          * @param d     a date in Unix date time format.
2730          *
2731          * @return  date string in database date format
2732          */
2733         function DBDate($d, $isfld=false) {
2734                 if (empty($d) && $d !== 0) {
2735                         return 'null';
2736                 }
2737                 if ($isfld) {
2738                         return $d;
2739                 }
2740                 if (is_object($d)) {
2741                         return $d->format($this->fmtDate);
2742                 }
2744                 if (is_string($d) && !is_numeric($d)) {
2745                         if ($d === 'null') {
2746                                 return $d;
2747                         }
2748                         if (strncmp($d,"'",1) === 0) {
2749                                 $d = _adodb_safedateq($d);
2750                                 return $d;
2751                         }
2752                         if ($this->isoDates) {
2753                                 return "'$d'";
2754                         }
2755                         $d = ADOConnection::UnixDate($d);
2756                 }
2758                 return adodb_date($this->fmtDate,$d);
2759         }
2761         function BindDate($d) {
2762                 $d = $this->DBDate($d);
2763                 if (strncmp($d,"'",1)) {
2764                         return $d;
2765                 }
2767                 return substr($d,1,strlen($d)-2);
2768         }
2770         function BindTimeStamp($d) {
2771                 $d = $this->DBTimeStamp($d);
2772                 if (strncmp($d,"'",1)) {
2773                         return $d;
2774                 }
2776                 return substr($d,1,strlen($d)-2);
2777         }
2780         /**
2781          * Converts a timestamp "ts" to a string that the database can understand.
2782          *
2783          * @param ts    a timestamp in Unix date time format.
2784          *
2785          * @return  timestamp string in database timestamp format
2786          */
2787         function DBTimeStamp($ts,$isfld=false) {
2788                 if (empty($ts) && $ts !== 0) {
2789                         return 'null';
2790                 }
2791                 if ($isfld) {
2792                         return $ts;
2793                 }
2794                 if (is_object($ts)) {
2795                         return $ts->format($this->fmtTimeStamp);
2796                 }
2798                 # strlen(14) allows YYYYMMDDHHMMSS format
2799                 if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) {
2800                         return adodb_date($this->fmtTimeStamp,$ts);
2801                 }
2803                 if ($ts === 'null') {
2804                         return $ts;
2805                 }
2806                 if ($this->isoDates && strlen($ts) !== 14) {
2807                         $ts = _adodb_safedate($ts);
2808                         return "'$ts'";
2809                 }
2810                 $ts = ADOConnection::UnixTimeStamp($ts);
2811                 return adodb_date($this->fmtTimeStamp,$ts);
2812         }
2814         /**
2815          * Also in ADORecordSet.
2816          * @param $v is a date string in YYYY-MM-DD format
2817          *
2818          * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2819          */
2820         static function UnixDate($v) {
2821                 if (is_object($v)) {
2822                 // odbtp support
2823                 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2824                         return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2825                 }
2827                 if (is_numeric($v) && strlen($v) !== 8) {
2828                         return $v;
2829                 }
2830                 if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", $v, $rr)) {
2831                         return false;
2832                 }
2834                 if ($rr[1] <= TIMESTAMP_FIRST_YEAR) {
2835                         return 0;
2836                 }
2838                 // h-m-s-MM-DD-YY
2839                 return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2840         }
2843         /**
2844          * Also in ADORecordSet.
2845          * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2846          *
2847          * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2848          */
2849         static function UnixTimeStamp($v) {
2850                 if (is_object($v)) {
2851                 // odbtp support
2852                 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2853                         return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2854                 }
2856                 if (!preg_match(
2857                         "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
2858                         ($v), $rr)) return false;
2860                 if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) {
2861                         return 0;
2862                 }
2864                 // h-m-s-MM-DD-YY
2865                 if (!isset($rr[5])) {
2866                         return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2867                 }
2868                 return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2869         }
2871         /**
2872          * Also in ADORecordSet.
2873          *
2874          * Format database date based on user defined format.
2875          *
2876          * @param v             is the character date in YYYY-MM-DD format, returned by database
2877          * @param fmt   is the format to apply to it, using date()
2878          *
2879          * @return a date formated as user desires
2880          */
2881         function UserDate($v,$fmt='Y-m-d',$gmt=false) {
2882                 $tt = $this->UnixDate($v);
2884                 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2885                 if (($tt === false || $tt == -1) && $v != false) {
2886                         return $v;
2887                 } else if ($tt == 0) {
2888                         return $this->emptyDate;
2889                 } else if ($tt == -1) {
2890                         // pre-TIMESTAMP_FIRST_YEAR
2891                 }
2893                 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2895         }
2897         /**
2898          *
2899          * @param v             is the character timestamp in YYYY-MM-DD hh:mm:ss format
2900          * @param fmt   is the format to apply to it, using date()
2901          *
2902          * @return a timestamp formated as user desires
2903          */
2904         function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) {
2905                 if (!isset($v)) {
2906                         return $this->emptyTimeStamp;
2907                 }
2908                 # strlen(14) allows YYYYMMDDHHMMSS format
2909                 if (is_numeric($v) && strlen($v)<14) {
2910                         return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2911                 }
2912                 $tt = $this->UnixTimeStamp($v);
2913                 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2914                 if (($tt === false || $tt == -1) && $v != false) {
2915                         return $v;
2916                 }
2917                 if ($tt == 0) {
2918                         return $this->emptyTimeStamp;
2919                 }
2920                 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2921         }
2923         function escape($s,$magic_quotes=false) {
2924                 return $this->addq($s,$magic_quotes);
2925         }
2927         /**
2928         * Quotes a string, without prefixing nor appending quotes.
2929         */
2930         function addq($s,$magic_quotes=false) {
2931                 if (!$magic_quotes) {
2932                         if ($this->replaceQuote[0] == '\\') {
2933                                 // only since php 4.0.5
2934                                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2935                                 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2936                         }
2937                         return  str_replace("'",$this->replaceQuote,$s);
2938                 }
2940                 // undo magic quotes for "
2941                 $s = str_replace('\\"','"',$s);
2943                 if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) {
2944                         // ' already quoted, no need to change anything
2945                         return $s;
2946                 } else {
2947                         // change \' to '' for sybase/mssql
2948                         $s = str_replace('\\\\','\\',$s);
2949                         return str_replace("\\'",$this->replaceQuote,$s);
2950                 }
2951         }
2953         /**
2954          * Correctly quotes a string so that all strings are escaped. We prefix and append
2955          * to the string single-quotes.
2956          * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
2957          *
2958          * @param s                     the string to quote
2959          * @param [magic_quotes]        if $s is GET/POST var, set to get_magic_quotes_gpc().
2960          *                              This undoes the stupidity of magic quotes for GPC.
2961          *
2962          * @return  quoted string to be sent back to database
2963          */
2964         function qstr($s,$magic_quotes=false) {
2965                 if (!$magic_quotes) {
2966                         if ($this->replaceQuote[0] == '\\'){
2967                                 // only since php 4.0.5
2968                                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2969                                 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2970                         }
2971                         return  "'".str_replace("'",$this->replaceQuote,$s)."'";
2972                 }
2974                 // undo magic quotes for "
2975                 $s = str_replace('\\"','"',$s);
2977                 if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) {
2978                         // ' already quoted, no need to change anything
2979                         return "'$s'";
2980                 } else {
2981                         // change \' to '' for sybase/mssql
2982                         $s = str_replace('\\\\','\\',$s);
2983                         return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2984                 }
2985         }
2988         /**
2989         * Will select the supplied $page number from a recordset, given that it is paginated in pages of
2990         * $nrows rows per page. It also saves two boolean values saying if the given page is the first
2991         * and/or last one of the recordset. Added by Iv├ín Oliva to provide recordset pagination.
2992         *
2993         * See docs-adodb.htm#ex8 for an example of usage.
2994         *
2995         * @param sql
2996         * @param nrows          is the number of rows per page to get
2997         * @param page           is the page number to get (1-based)
2998         * @param [inputarr]     array of bind variables
2999         * @param [secs2cache]           is a private parameter only used by jlim
3000         * @return               the recordset ($rs->databaseType == 'array')
3001         *
3002         * NOTE: phpLens uses a different algorithm and does not use PageExecute().
3003         *
3004         */
3005         function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) {
3006                 global $ADODB_INCLUDED_LIB;
3007                 if (empty($ADODB_INCLUDED_LIB)) {
3008                         include(ADODB_DIR.'/adodb-lib.inc.php');
3009                 }
3010                 if ($this->pageExecuteCountRows) {
3011                         $rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3012                 } else {
3013                         $rs = _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3014                 }
3015                 return $rs;
3016         }